home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 25 / Cream of the Crop 25.iso / doom / axxwar_1.zip / SOURCES / CUJOAI.QC < prev    next >
Text File  |  1997-02-21  |  88KB  |  3,045 lines

  1. /*════════════════════════════════════════════════════════════════════════╗
  2.   ║                                                                       ║
  3.   ║                            CUJO BOT ver 1.4                           ║
  4.   ║                                                                       ║
  5.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  6.  
  7. /*
  8.   CUJO 1.4 - Changes
  9.  
  10.   Added swim code
  11.   New movetogoal code
  12.   fixed ai_face for stuck code
  13.   Yaw rate is now 90 degress
  14.  
  15.   CUJO 1.3A - Changes
  16.  
  17.   fixed velocities for jumping down and into water - minimum of 50 forward
  18.   velocity added to all jumps, scaled down distance multiplier a tad for each
  19.   type of jump
  20.  
  21.   Cujo no longer teleports to owner if he is staying
  22.  
  23.   In deathmatch, Cujo's will attack one another (really, really cool)
  24. */
  25.  
  26. // distance from player before cujo initiates a teleport
  27. float   teleport_dist  = 1500;
  28.  
  29. // Prototypes
  30.  
  31. float   ()                                             CUJO_FindTarget; //
  32. void    ()                                             CUJO_ai_stand;   //
  33. void    ()                                             CUJO_pain;       //
  34. void    ()                                             CUJO_die;        //
  35. void    ()                                             CUJO_SightSound; //
  36. void    ()                                             CUJO_FoundTarget;//
  37. void    ()                                             CUJO_HuntTarget; //
  38. void    (float dist)                                   CUJO_ai_walk;    //
  39. void    ()                                             CUJO_ai_turn;    //
  40. void    (float dist)                                   CUJO_ai_run;     //
  41. void    (float dist)                                   CUJO_ai_follow;  //
  42. void    ()                                             CUJO_ai_face;
  43. void    (float d)                                      CUJO_ai_charge;
  44. void    (void () thinkst)                              CUJO_CheckRefire;//
  45. void    ()                                             CUJO_SelfDeactivate;//
  46.  
  47. void    ()                                             CUJO_TeleportToOwner;//
  48. vector  ()                                             CUJO_SpawnPos;
  49. float   (vector SpawnPos)                              CUJO_CheckSpawnPos;
  50.  
  51. // Cujo Prototypes - called by player
  52.  
  53. void () CUJO_Precache;
  54. void () CUJO_Activate;        // Activate Cujo
  55. void () CUJO_Deactivate;    // DeActivate Cujo
  56. void () CUJO_Toggle;        // Toggle Cujo on and off
  57. void () CUJO_AttackToggle;    // Toggle Cujo's auto-firing in auto mode
  58. /*
  59. void ()    CUJO_Attack;        // Have Cujo fire at its current target in auto mode
  60. */
  61. void ()    CUJO_TeleportHome;    // Have Cujo teleport back to its owner
  62. void ()    CUJO_LightToggle;    // Have Cujo teleport back to its owner
  63. void () CUJO_SetDogView;
  64. void () CUJO_SetPlayerView;
  65. float () CUJO_JumpAI;
  66.  
  67. // Global Variables
  68.  
  69. float   goal_range;
  70. float   goal_vis;
  71. float   goal_infront;
  72. float   goal_yaw;
  73. vector  old_player_angles;
  74. string  temp_text;
  75.  
  76. // new for Cujo jumping AI
  77. //float   forward_jump_vel;
  78. //float   upward_jump_vel;
  79.  
  80. // Constants
  81.  
  82. float   nextthinktime = 0.01;
  83.  
  84. $cd /raid/quake/id1/models/dog
  85. $origin 0 0 24
  86. $base base
  87. $skin skin
  88.  
  89. $frame attack1 attack2 attack3 attack4 attack5 attack6 attack7 attack8
  90.  
  91. $frame death1 death2 death3 death4 death5 death6 death7 death8 death9
  92.  
  93. $frame deathb1 deathb2 deathb3 deathb4 deathb5 deathb6 deathb7 deathb8
  94. $frame deathb9
  95.  
  96. $frame pain1 pain2 pain3 pain4 pain5 pain6
  97.  
  98. $frame painb1 painb2 painb3 painb4 painb5 painb6 painb7 painb8 painb9 painb10
  99. $frame painb11 painb12 painb13 painb14 painb15 painb16
  100.  
  101. $frame run1 run2 run3 run4 run5 run6 run7 run8 run9 run10 run11 run12
  102.  
  103. $frame leap1 leap2 leap3 leap4 leap5 leap6 leap7 leap8 leap9
  104.  
  105. $frame stand1 stand2 stand3 stand4 stand5 stand6 stand7 stand8 stand9
  106.  
  107. $frame walk1 walk2 walk3 walk4 walk5 walk6 walk7 walk8
  108.  
  109.  
  110. void() CUJO_run1;
  111. void () CUJO_JumpTouch;
  112. float (float start_dist) CUJO_FindLedge;
  113. float (float dist) CUJO_JumpObstructed;
  114. void () CUJO_leap1;
  115.  
  116. /*
  117. ===============
  118. CUJO_allstop
  119.  
  120. sets the bots velocity to 0
  121. ===============
  122. */
  123.  
  124. void () CUJO_allstop =
  125. {
  126.   if ((self.flags & FL_ONGROUND))
  127.     self.velocity = '0 0 0';
  128. };
  129.  
  130. /*
  131. ===============
  132. CUJO_slowdown
  133.  
  134. halves the bot's velocity. used to make him less likely to fall off edges before jumping
  135. ===============
  136. */
  137. void () CUJO_slowdown =
  138. {
  139.   self.velocity = self.velocity * 0.5;
  140. };
  141.  
  142. /*
  143. ===============
  144. CUJO_CheckGap
  145.  
  146. determines if there is an unwalkable gap directly in front of the bot
  147. checking begins at dist units in front of the bot
  148. ===============
  149. */
  150. float (float dist) CUJO_CheckGap =
  151. {
  152.   local   float   grade, out_dist, down_dist, solid_count;
  153.   local   float   i, trace_dist, fwd_inc, tf_min, tf_max;
  154.   local   vector  org, end;
  155.  
  156. // call makevectors before calling CheckGap!!!!!!!!!
  157. //  makevectors (self.angles);
  158.  
  159.   org = self.origin;
  160.  
  161.   // check 64 units in front, foot level
  162.   traceline (org, org - '0 0 24' + v_forward * (dist), TRUE, self);
  163.   if (trace_fraction != 1.0)
  164.     return FALSE;
  165.  
  166.   // check 64 units in front, waist level
  167.   traceline (org, org + v_forward * (dist), TRUE, self);
  168.   if (trace_fraction != 1.0)
  169.   return FALSE;
  170.  
  171.   // check 64 units in front, eye level
  172.   traceline (org, org + '0 0 16' + v_forward * (dist), TRUE, self);
  173.   if (trace_fraction != 1.0)
  174.   return FALSE;
  175.  
  176.   // no wall detected, so continue looking for gap
  177.   trace_dist = 1000;
  178.   fwd_inc = 8;
  179.   // begin dist units in front of bot at waist level
  180.   org = self.origin + v_forward * dist;
  181.   // end trace_dist units below that
  182.   end = org;
  183.   end_z = end_z - trace_dist;
  184.   solid_count = out_dist = down_dist = 0;
  185.  
  186.   tf_min = trace_dist;
  187.   tf_max = 0;
  188.  
  189.   i = 0;
  190.   // continue tracing 8 times, going further out each time
  191.   while (i < 4)
  192.   {
  193.     // trace second line down, add height to down_dist, increment out_dist
  194.     org = org + v_forward * fwd_inc;
  195.     end = end + v_forward * fwd_inc;
  196.     traceline (org, end, TRUE, self);
  197.  
  198.     // if first trace, set tf_min
  199.     down_dist = trace_fraction * trace_dist;
  200.     if (i == 0)
  201.       tf_min = down_dist;
  202.     else if (i == 3)
  203.       tf_max = down_dist;
  204.  
  205.     // is their a solid object here < 24 units below bot's feet?
  206.     if (trace_fraction * trace_dist < 48)
  207.       solid_count = solid_count + 1;
  208.     out_dist = out_dist + fwd_inc;
  209.  
  210.     i = i + 1;
  211.   }
  212.  
  213.   down_dist = tf_max - tf_min;
  214.   grade = down_dist / out_dist;
  215.  
  216.   // check for upward slope
  217. //  if (tf_max < tf_min)
  218. //    return FALSE;
  219.  
  220.   // are both max and min almost equal, but still far below?
  221.   // if so, grade will be close to 0, but there is still a
  222.   // dropoff
  223.  
  224.   if ((tf_max > (dist + 16)) && (tf_min > (dist + 16)) && (grade < 0.25))
  225.     return TRUE;
  226.  
  227.   if ((grade > 1.75) && (solid_count < 1))
  228.     return TRUE;
  229.   else
  230.     return FALSE;
  231.  
  232. };
  233.  
  234. /*
  235. ===============
  236. CUJO_CheckWater
  237.  
  238. determines if there is an unwalkable gap directly in front of the bot
  239. checking begins at dist units in front of the bot
  240. ===============
  241. */
  242. float (float dist) CUJO_CheckWater =
  243. {
  244.   local   float   grade, out_dist, down_dist, water_count;
  245.   local   float   i, trace_dist, fwd_inc, tf_min, tf_max;
  246.   local   vector  org, end;
  247.  
  248. // call makevectors before calling CheckWater!!!!!!!!!
  249. //  makevectors (self.angles);
  250.  
  251.   org = self.origin;
  252.  
  253.   // check 64 units in front, foot level
  254.   traceline (org, org - '0 0 24' + v_forward * (dist), TRUE, self);
  255.   if (trace_fraction != 1.0)
  256.   {
  257. //    return FALSE;
  258.  
  259.     // check 64 units in front, waist level
  260.     traceline (org, org + v_forward * (dist), TRUE, self);
  261.     if (trace_fraction != 1.0)
  262.     {
  263. //    return FALSE;
  264.  
  265.       // check 64 units in front, eye level
  266.       traceline (org, org + '0 0 16' + v_forward * (dist), TRUE, self);
  267.       if (trace_fraction != 1.0)
  268.         return FALSE;
  269.     }
  270.   }
  271.   // no wall detected, so continue looking for water
  272.   trace_dist = 1000;
  273.   fwd_inc = 8;
  274.   // begin dist units in front of bot at waist level
  275.   org = self.origin + v_forward * dist;
  276.   // end trace_dist units below that
  277.   end = org;
  278.   end_z = end_z - trace_dist;
  279.   water_count = 0;
  280.  
  281.   i = 0;
  282.   // continue tracing 4 times, going further out each time
  283.   while (i < 4)
  284.   {
  285.     // trace second line down, add height to down_dist, increment out_dist
  286.     org = org + v_forward * fwd_inc;
  287.     end = end + v_forward * fwd_inc;
  288.     traceline (org, end, TRUE, self);
  289.  
  290.     if (trace_inwater)
  291.       water_count = water_count + 1;
  292.  
  293.     i = i + 1;
  294.   }
  295.  
  296.   if (water_count >= 3)
  297.     return TRUE;
  298.   else
  299.     return FALSE;
  300.  
  301. };
  302.  
  303. /*
  304. ===============
  305. CUJO_movetogoal
  306.  
  307. moves the bot using his velocity vectors, rather than discretely
  308. ===============
  309. */
  310.  
  311. void (float d) CUJO_movetogoal =
  312. {
  313.   bprint ("movetogoal not implemented in Quake C! Remove CUJO_movetogoal!\n");
  314. };
  315.  
  316. /*╔═══════════════════════════════════════════════════════════════════════╗
  317.   ║                                                                       ║
  318.   ║                 Routines called by Cujo, self = Cujo                  ║
  319.   ║                                                                       ║
  320.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  321.  
  322. /*
  323. ======================
  324. CUJO_Msg
  325.  
  326. bprints the passed strings, without a newline
  327. ======================
  328. */
  329.  
  330. void (string str1) CUJO_Msg =
  331. {
  332.   if (!self.CUJO_verbose) return;
  333.  
  334.   if (str1 != "")
  335.     bprint (str1);
  336. };
  337.  
  338. /*
  339. ======================
  340. CUJO_Msgln
  341.  
  342. bprints the passed strings and a newline
  343. ======================
  344. */
  345.  
  346. void (string str1) CUJO_Msgln =
  347. {
  348.   if (!self.CUJO_verbose) return;
  349.  
  350.   if (str1 != "")
  351.     bprint (str1);
  352.  
  353.   bprint ("\n");
  354. };
  355.  
  356. /*
  357. ======================
  358. CUJO_WaterLevel
  359. ======================
  360. */
  361. float (entity ent) CUJO_WaterLevel =
  362. {
  363.   local   float   pc;
  364.  
  365.   makevectors (ent.angles);
  366.   pc = pointcontents (ent.origin + v_forward * 16 + v_up * 10);
  367.  
  368.   // all the way under
  369.   if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME) || (pc == CONTENT_LAVA))
  370.     return 3;
  371.  
  372.   pc = pointcontents (ent.origin + v_forward * 16 + v_up * 4);
  373.   // head out
  374.   if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME) || (pc == CONTENT_LAVA))
  375.     return 2;
  376.  
  377.   pc = pointcontents (ent.origin + v_forward * 16 - v_up * 12);
  378.   // lower body and/or feet
  379.   if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME) || (pc == CONTENT_LAVA))
  380.     return 1;
  381.  
  382.   return 0;
  383. };
  384.  
  385. /*╔═══════════════════════════════════════════════════════════════════════╗
  386.   ║                                                                       ║
  387.   ║ CUJO_PrintCujoStatus                                                  ║
  388.   ║                                                                       ║
  389.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  390.  
  391. void () CUJO_PrintCujoStatus =
  392.  
  393. {
  394.   if (!deathmatch)
  395.     sprint (self, "Cujo is not here.\n");
  396.   else
  397.   {
  398.     if (self.Cujo_avail)
  399.       sprint (self, "Cujo is not here.\n");
  400.     else
  401.       sprint (self, "Cujo is not available.\n");
  402.   }
  403. };
  404.  
  405. /*╔═══════════════════════════════════════════════════════════════════════╗
  406.   ║                                                                       ║
  407.   ║ CUJO_ResetGoalEntity                                                  ║
  408.   ║                                                                       ║
  409.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  410.  
  411. void () CUJO_ResetGoalEntity =
  412.  
  413. {
  414.   self.oldenemy = self.enemy;
  415.   self.enemy = world;
  416.  
  417.   self.goalentity = self.owner;
  418.   self.movetarget = self.owner;
  419. };
  420.  
  421. /*╔═══════════════════════════════════╗
  422.   ║                                   ║
  423.   ║ Cujo_bite                         ║
  424.   ║                                   ║
  425.   ╚═══════════════════════════════════╝*/
  426.  
  427. void() CUJO_bite =
  428. {
  429.   local vector    delta;
  430.   local float     ldmg;
  431.  
  432.   if (!self.enemy) return;
  433.  
  434.   CUJO_ai_charge(10);
  435.  
  436.   if (!CanDamage (self.enemy, self)) return;
  437.  
  438.   delta = self.enemy.origin - self.origin;
  439.  
  440.   if (vlen(delta) > 100) return;
  441.  
  442.   // does twice the damage of a normal dog and has a random chance
  443.   // of gibbing!
  444.  
  445.   if ((self.enemy.health < 10) && (random () < 0.1))
  446.   {
  447.     {
  448.       ldmg = 50 * random ();
  449.     }
  450.   }
  451.   else
  452.   {
  453.     ldmg = (random() + random() + random()) * 16;
  454.   }
  455.  
  456.   T_Damage (self.enemy, self, self, ldmg);
  457. };
  458.  
  459. /*╔═══════════════════════════════════╗
  460.   ║                                   ║
  461.   ║ Cujo_ChangeYaw                    ║
  462.   ║                                   ║
  463.   ╚═══════════════════════════════════╝*/
  464.  
  465. void() CUJO_ChangeYaw =
  466. {
  467.   local float        ideal, move;
  468.  
  469.   current_yaw = anglemod (self.angles_y);
  470.   ideal = self.ideal_yaw;
  471.  
  472.   if (current_yaw == ideal)
  473.     return;
  474.  
  475.   move = ideal - current_yaw;
  476.   if (ideal > current_yaw)
  477.   {
  478.     if (move > 180)
  479.       move = move - 360;
  480.   }
  481.   else
  482.   {
  483.     if (move < -180)
  484.       move = move + 360;
  485.   }
  486.  
  487.   if (move > 0)
  488.   {
  489.     if (move > self.yaw_speed)
  490.     move = self.yaw_speed;
  491.   }
  492.   else
  493.   {
  494.     if (move < 0-self.yaw_speed )
  495.     move = 0-self.yaw_speed;
  496.   }
  497.  
  498.   current_yaw = anglemod (current_yaw + move);
  499.   if (fabs (ideal - current_yaw) < self.yaw_speed)
  500.     current_yaw = ideal;
  501.  
  502.  
  503.   self.angles_y = current_yaw;
  504. };
  505.  
  506. /*╔═══════════════════════════════════╗
  507.   ║                                   ║
  508.   ║ Cujo_jumptouch                    ║
  509.   ║                                   ║
  510.   ╚═══════════════════════════════════╝*/
  511.  
  512. void()    CUJO_JumpTouch =
  513. {
  514.   local    float    ldmg;
  515.  
  516.   if (self.health <= 0) return;
  517.  
  518.   if (other.takedamage)
  519.   {
  520.     if ( vlen(self.velocity) > 300 )
  521.     {
  522.       // Cujo now has a chance to gib on a jumping attack
  523.       ldmg = 20 + 20*random();
  524.  
  525.       T_Damage (other, self, self, ldmg);
  526.     }
  527.   }
  528.  
  529.   self.touch = SUB_Null;
  530.   self.think = CUJO_run1;
  531.   self.nextthink = time + nextthinktime;
  532. };
  533.  
  534.  
  535. void() CUJO_stand1    =[    $stand1,    CUJO_stand2    ] {CUJO_ai_stand();};
  536. void() CUJO_stand2    =[    $stand2,    CUJO_stand3    ] {CUJO_ai_stand();};
  537. void() CUJO_stand3    =[    $stand3,    CUJO_stand4    ] {CUJO_ai_stand();};
  538. void() CUJO_stand4    =[    $stand4,    CUJO_stand5    ] {CUJO_ai_stand();};
  539. void() CUJO_stand5    =[    $stand5,    CUJO_stand6    ] {CUJO_ai_stand();};
  540. void() CUJO_stand6    =[    $stand6,    CUJO_stand7    ] {CUJO_ai_stand();};
  541. void() CUJO_stand7    =[    $stand7,    CUJO_stand8    ] {CUJO_ai_stand();};
  542. void() CUJO_stand8    =[    $stand8,    CUJO_stand9    ] {CUJO_ai_stand();};
  543. void() CUJO_stand9    =[    $stand9,    CUJO_stand1    ] {CUJO_ai_stand();};
  544.  
  545. /*╔═══════════════════════════════════╗
  546.   ║                                   ║
  547.   ║ Cujo_walk1                        ║
  548.   ║                                   ║
  549.   ╚═══════════════════════════════════╝*/
  550.  
  551.  
  552. void() CUJO_walk1    =[    $walk1 ,    CUJO_walk2    ]
  553. {
  554.   if (random() < 0.2)
  555.     sound (self, CHAN_VOICE, "dog/idle.wav", 1, ATTN_IDLE);
  556.  
  557.   CUJO_ai_walk (8);
  558. };
  559.  
  560.  
  561. void() CUJO_walk2    =[    $walk2 ,    CUJO_walk3    ] {CUJO_ai_walk(8);};
  562. void() CUJO_walk3    =[    $walk3 ,    CUJO_walk4    ] {CUJO_ai_walk(8);};
  563. void() CUJO_walk4    =[    $walk4 ,    CUJO_walk5    ] {CUJO_ai_walk(8);};
  564. void() CUJO_walk5    =[    $walk5 ,    CUJO_walk6    ] {CUJO_ai_walk(8);};
  565. void() CUJO_walk6    =[    $walk6 ,    CUJO_walk7    ] {CUJO_ai_walk(8);};
  566. void() CUJO_walk7    =[    $walk7 ,    CUJO_walk8    ] {CUJO_ai_walk(8);};
  567. void() CUJO_walk8    =[    $walk8 ,    CUJO_walk1    ] {CUJO_ai_walk(8);};
  568.  
  569. /*╔═══════════════════════════════════╗
  570.   ║                                   ║
  571.   ║ Cujo_run1                         ║
  572.   ║                                   ║
  573.   ╚═══════════════════════════════════╝*/
  574.  
  575. void() CUJO_run1        =[    $run1  ,    CUJO_run2    ]
  576. {
  577.   if (random() < 0.2)
  578.     sound (self, CHAN_VOICE, "dog/idle.wav", 1, ATTN_IDLE);
  579.  
  580.   CUJO_ai_run (16);
  581. };
  582.  
  583. void() CUJO_run2        =[    $run2  ,    CUJO_run3    ] {CUJO_ai_run(32);};
  584. void() CUJO_run3        =[    $run3  ,    CUJO_run4    ] {CUJO_ai_run(32);};
  585. void() CUJO_run4        =[    $run4  ,    CUJO_run5    ] {CUJO_ai_run(20);};
  586. void() CUJO_run5        =[    $run5  ,    CUJO_run6    ] {CUJO_ai_run(64);};
  587. void() CUJO_run6        =[    $run6  ,    CUJO_run7    ] {CUJO_ai_run(32);};
  588. void() CUJO_run7        =[    $run7  ,    CUJO_run8    ] {CUJO_ai_run(16);};
  589. void() CUJO_run8        =[    $run8  ,    CUJO_run9    ] {CUJO_ai_run(32);};
  590. void() CUJO_run9        =[    $run9  ,    CUJO_run10    ] {CUJO_ai_run(32);};
  591. void() CUJO_run10    =[    $run10  ,    CUJO_run11    ] {CUJO_ai_run(20);};
  592. void() CUJO_run11    =[    $run11  ,    CUJO_run12    ] {CUJO_ai_run(64);};
  593. void() CUJO_run12    =[    $run12  ,    CUJO_run1    ] {CUJO_ai_run(32);};
  594.  
  595. void()    CUJO_follow1    =[    $run1  ,    CUJO_follow2    ]
  596. {
  597.   if (random() < 0.2)
  598.     sound (self, CHAN_VOICE, "dog/idle.wav", 1, ATTN_IDLE);
  599.  
  600.   CUJO_ResetGoalEntity ();
  601.  
  602.   CUJO_ai_follow (18);
  603. };
  604. void()    CUJO_follow2    =[    $run2  ,    CUJO_follow3    ] {CUJO_ai_follow (32);};
  605. void()    CUJO_follow3    =[    $run3  ,    CUJO_follow4    ] {CUJO_ai_follow (32);};
  606. void()    CUJO_follow4    =[    $run4  ,    CUJO_follow5    ] {CUJO_ai_follow (20);};
  607. void()    CUJO_follow5    =[    $run5  ,    CUJO_follow6    ] {CUJO_ai_follow (64);};
  608. void()    CUJO_follow6    =[    $run6  ,    CUJO_follow7    ] {CUJO_ai_follow (32);};
  609. void()    CUJO_follow7    =[    $run7  ,    CUJO_follow8    ] {CUJO_ai_follow (16);};
  610. void()    CUJO_follow8    =[    $run8  ,    CUJO_follow9    ] {CUJO_ai_follow (32);};
  611. void()  CUJO_follow9    =[    $run9  ,    CUJO_follow10    ] {CUJO_ai_follow(32);};
  612. void()  CUJO_follow10    =[    $run10  ,    CUJO_follow11    ] {CUJO_ai_follow(20);};
  613. void()  CUJO_follow11    =[    $run11  ,    CUJO_follow12    ] {CUJO_ai_follow(64);};
  614. void()  CUJO_follow12    =[    $run12  ,    CUJO_follow1    ] {CUJO_ai_follow(32);};
  615.  
  616. /*╔═══════════════════════════════════╗
  617.   ║                                   ║
  618.   ║ Cujo_atta1                        ║
  619.   ║                                   ║
  620.   ╚═══════════════════════════════════╝*/
  621.  
  622. void() CUJO_atta1    =[    $attack1,    CUJO_atta3    ]
  623. {
  624.   ai_charge (32);
  625.   self.nextthink = time + 0.025;
  626. };
  627. void() CUJO_atta2    =[    $attack2,    CUJO_atta3    ]
  628. {
  629.   ai_charge (32);
  630.   self.nextthink = time + 0.025;
  631. };
  632. void() CUJO_atta3    =[    $attack3,    CUJO_atta4    ]
  633. {
  634.   ai_charge (32);
  635.   self.nextthink = time + 0.025;
  636. };
  637. void() CUJO_atta4    =[    $attack4,    CUJO_atta6    ]
  638. {
  639.   sound (self, CHAN_VOICE, "dog/dattack1.wav", 1, ATTN_NORM);
  640.   CUJO_bite();
  641.   self.nextthink = time + 0.025;
  642. };
  643.  
  644. void() CUJO_atta5    =[    $attack5,    CUJO_atta6    ]
  645. {
  646.   ai_charge (32);
  647.   self.nextthink = time + 0.025;
  648. };
  649. void() CUJO_atta6    =[    $attack6,    CUJO_atta8    ]
  650. {
  651.   ai_charge (32);
  652.   self.nextthink = time + 0.025;
  653. };
  654. void() CUJO_atta7    =[    $attack7,    CUJO_atta8    ]
  655. {
  656.   ai_charge (32);
  657.   self.nextthink = time + 0.025;
  658. };
  659. void() CUJO_atta8    =[    $attack8,    CUJO_run1       ]
  660. {
  661.   local   float   r;
  662.  
  663.   if (self.goalentity.classname == "dog_food")
  664.   {
  665.     // CUJO 1.3 - fix for BodyQue bug
  666.     // don't remove a cujo or a player head, because they are in the
  667.     // body que, simply change their classname to "head" and model to
  668.     // "" and cujo will ignore them
  669.     if ((self.goalentity.model == "progs/h_dog.mdl") ||
  670.        (self.goalentity.model == "progs/player.mdl"))
  671.     {
  672.       self.goalentity.classname = "head";
  673.       setmodel (self.goalentity, "");
  674.     }
  675.     else
  676.       remove (self.goalentity);
  677.     self.health = self.health + 15;
  678.  
  679.     if (self.health > 100)
  680.       self.health = 100;
  681.  
  682.     r = random ();
  683.  
  684.     if (r < 0.25)
  685.       sprint (self.owner, "Cujo likes his Gibby-chow!\n");
  686.     else if (r < 0.5)
  687.       sprint (self.owner, "Dogs love a Gibby-Treat!\n");
  688.     else if (r < 0.75)
  689.       sprint (self.owner, "Cujo ate some gibs.\n");
  690.     else
  691.       sprint (self.owner, "Cujo's eating his enemies again...\n");
  692.  
  693.   }
  694.   else
  695.     CUJO_ai_charge (10);
  696.  
  697.   self.nextthink = time + 0.025;
  698.  
  699.   CUJO_CheckRefire (CUJO_atta1);
  700. };
  701.  
  702. /*╔═══════════════════════════════════╗
  703.   ║                                   ║
  704.   ║ Cujo_leap1                        ║
  705.   ║                                   ║
  706.   ╚═══════════════════════════════════╝*/
  707.  
  708. void() CUJO_leap1    =[    $leap1,        CUJO_leap2    ] {CUJO_ai_face();};
  709. void() CUJO_leap2    =[    $leap2,        CUJO_leap3    ]
  710. {
  711.   CUJO_ai_face();
  712.   self.touch = CUJO_JumpTouch;
  713.  
  714.   makevectors (self.angles);
  715.  
  716.   self.origin_z = self.origin_z + 1;
  717.   self.velocity = v_forward * self.forward_jump_vel + v_up * self.upward_jump_vel;
  718.  
  719.   if (self.flags & FL_ONGROUND)
  720.     self.flags = self.flags - FL_ONGROUND;
  721. };
  722.  
  723. void() CUJO_leap3    =[    $leap3,        CUJO_leap4    ] {};
  724. void() CUJO_leap4    =[    $leap4,        CUJO_leap5    ] {};
  725. void() CUJO_leap5    =[    $leap5,        CUJO_leap6    ] {};
  726. void() CUJO_leap6    =[    $leap6,        CUJO_leap7    ] {};
  727. void() CUJO_leap7    =[    $leap7,        CUJO_leap8    ] {};
  728. void() CUJO_leap8    =[    $leap8,        CUJO_leap9    ] {};
  729. void() CUJO_leap9    =[    $leap9,        CUJO_leap9    ] {};
  730.  
  731. /*╔═══════════════════════════════════╗
  732.   ║                                   ║
  733.   ║ Cujo_pain1                        ║
  734.   ║                                   ║
  735.   ╚═══════════════════════════════════╝*/
  736.  
  737. void() CUJO_pain1    =[    $pain1 ,    CUJO_pain2    ]
  738. {
  739.   self.nextthink = time + 0.001;
  740. };
  741. void() CUJO_pain2    =[    $pain2 ,    CUJO_pain3    ]
  742. {
  743.   self.nextthink = time + 0.001;
  744. };
  745. void() CUJO_pain3    =[    $pain3 ,    CUJO_pain4    ]
  746. {
  747.   self.nextthink = time + 0.001;
  748. };
  749. void() CUJO_pain4    =[    $pain4 ,    CUJO_pain5    ]
  750. {
  751.   self.nextthink = time + 0.001;
  752. };
  753. void() CUJO_pain5    =[    $pain5 ,    CUJO_pain6    ]
  754. {
  755.   self.nextthink = time + 0.001;
  756. };
  757. void() CUJO_pain6    =[    $pain6 ,    CUJO_run1    ]
  758. {
  759.   self.nextthink = time + 0.001;
  760. };
  761.  
  762. void() CUJO_painb1    =[    $painb1 ,    CUJO_painb3    ]
  763. {
  764.   self.nextthink = time + 0.001;
  765. };
  766. void() CUJO_painb2    =[    $painb2 ,    CUJO_painb3    ]
  767. {
  768.   self.nextthink = time + 0.001;
  769. };
  770. void() CUJO_painb3    =[    $painb3 ,    CUJO_painb4     ]
  771. {
  772.   ai_pain (4);
  773.   self.nextthink = time + 0.001;
  774. };
  775. void() CUJO_painb4    =[    $painb4 ,    CUJO_painb5    ]
  776. {
  777.   ai_pain (12);
  778.   self.nextthink = time + 0.001;
  779. };
  780. void() CUJO_painb5    =[    $painb5 ,    CUJO_painb6    ]
  781. {
  782.   ai_pain (12);
  783.   self.nextthink = time + 0.001;
  784. };
  785. void() CUJO_painb6    =[    $painb6 ,    CUJO_painb8    ]
  786. {
  787.   ai_pain (2);
  788.   self.nextthink = time + 0.001;
  789. };
  790. void() CUJO_painb7    =[    $painb7 ,    CUJO_painb8    ]
  791. {
  792.   self.nextthink = time + 0.001;
  793. };
  794. void() CUJO_painb8    =[    $painb8 ,    CUJO_painb10]
  795. {
  796.   ai_pain (4);
  797.   self.nextthink = time + 0.001;
  798. };
  799. void() CUJO_painb9    =[    $painb9 ,    CUJO_painb10    ]
  800. {
  801.   self.nextthink = time + 0.001;
  802. };
  803. void() CUJO_painb10    =[    $painb10 ,    CUJO_painb12    ]
  804. {
  805.   ai_pain (10);
  806.   self.nextthink = time + 0.001;
  807. };
  808. void() CUJO_painb11    =[    $painb11 ,    CUJO_painb12    ]
  809. {
  810.   self.nextthink = time + 0.001;
  811. };
  812. void() CUJO_painb12    =[    $painb12 ,    CUJO_painb14    ]
  813. {
  814.   self.nextthink = time + 0.001;
  815. };
  816. void() CUJO_painb13    =[    $painb13 ,    CUJO_painb14    ]
  817. {
  818.   self.nextthink = time + 0.001;
  819. };
  820. void() CUJO_painb14    =[    $painb14 ,    CUJO_painb16    ]
  821. {
  822.   self.nextthink = time + 0.001;
  823. };
  824. void() CUJO_painb15    =[    $painb15 ,    CUJO_painb16    ]
  825. {
  826.   self.nextthink = time + 0.001;
  827. };
  828. void() CUJO_painb16    =[    $painb16 ,    CUJO_run1    ]
  829. {
  830.   self.nextthink = time + 0.001;
  831. };
  832.  
  833. void() CUJO_pain =
  834. {
  835.   if (self.dmg_inflictor.classname == "fire")
  836.   {
  837.     if (random() > 0.5)
  838.     {
  839.       if (self.pain_finished > time)
  840.         return;
  841.  
  842.       if (random () > 0.2)
  843.       {
  844.         CUJO_pain1 ();
  845.         self.pain_finished = time + 0.6;
  846.       }
  847.       else
  848.       {
  849.         self.pain_finished = time + 1.6;
  850.         CUJO_painb1 ();
  851.       }
  852.  
  853.       sound (self, CHAN_VOICE, "dog/dpain1.wav", 1, ATTN_NORM);
  854.     }
  855.   }
  856.   else
  857.   {
  858.     sound (self, CHAN_VOICE, "dog/dpain1.wav", 1, ATTN_NORM);
  859.  
  860.     if (random() > 0.5)
  861.       CUJO_pain1 ();
  862.     else
  863.       CUJO_painb1 ();
  864.   }
  865. };
  866.  
  867. /*╔═══════════════════════════════════╗
  868.   ║                                   ║
  869.   ║ Cujo_die                          ║
  870.   ║                                   ║
  871.   ║ Cujo deactivates self in last     ║
  872.   ║ frame                             ║
  873.   ║                                   ║
  874.   ╚═══════════════════════════════════╝*/
  875.  
  876. void() CUJO_die1        =[    $death1,    CUJO_die2    ] {};
  877. void() CUJO_die2        =[    $death2,    CUJO_die3    ] {};
  878. void() CUJO_die3        =[    $death3,    CUJO_die4    ] {};
  879. void() CUJO_die4        =[    $death4,    CUJO_die5    ] {};
  880. void() CUJO_die5        =[    $death5,    CUJO_die6    ] {};
  881. void() CUJO_die6        =[    $death6,    CUJO_die7    ] {};
  882. void() CUJO_die7        =[    $death7,    CUJO_die8    ] {};
  883. void() CUJO_die8        =[    $death8,    CUJO_die9    ] {};
  884. void() CUJO_die9        =[    $death9,    CUJO_die9    ] {CUJO_SelfDeactivate ();};
  885. /*
  886. // used to set the dead-entity body to the correct final death frame
  887. // then set up the next think to make the body disappear
  888. void() CUJO_die10        =[    $death9,    CUJO_die10    ]
  889. {
  890.   self.nextthink = time + 120;  // wait 2 minutes till body disappears
  891.   self.think = SUB_Remove;
  892. };
  893. */
  894. void() CUJO_dieb1        =[    $deathb1,    CUJO_dieb2    ] {};
  895. void() CUJO_dieb2        =[    $deathb2,    CUJO_dieb3    ] {};
  896. void() CUJO_dieb3        =[    $deathb3,    CUJO_dieb4    ] {};
  897. void() CUJO_dieb4        =[    $deathb4,    CUJO_dieb5    ] {};
  898. void() CUJO_dieb5        =[    $deathb5,    CUJO_dieb6    ] {};
  899. void() CUJO_dieb6        =[    $deathb6,    CUJO_dieb7    ] {};
  900. void() CUJO_dieb7        =[    $deathb7,    CUJO_dieb8    ] {};
  901. void() CUJO_dieb8        =[    $deathb8,    CUJO_dieb9    ] {};
  902. void() CUJO_dieb9        =[    $deathb9,    CUJO_dieb9    ] {CUJO_SelfDeactivate ();};
  903. /*
  904. // used to set the dead-entity body to the correct final death frame
  905. // then set up the next think to make the body disappear
  906. void() CUJO_dieb10            =[    $deathb9,    CUJO_dieb10    ]
  907. {
  908.   self.nextthink = time + 120;  // wait 2 minutes till body disappears
  909.   self.think = SUB_Remove;
  910. };
  911. */
  912.  
  913. void() CUJO_die =
  914. {
  915. // check for gib
  916.   if (self.health < -35)
  917.   {
  918.     sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);
  919.     ThrowGib ("progs/gib3.mdl", self.health);
  920.     ThrowGib ("progs/gib3.mdl", self.health);
  921.     ThrowGib ("progs/gib3.mdl", self.health);
  922.  
  923.     ThrowHead ("progs/h_cujo.mdl", self.health);
  924.  
  925.     // Cujo heads only lay around for 2 minutes, then POOF!
  926. //    self.nextthink = time + 120;
  927. //    self.think = SUB_Remove;
  928.  
  929.     CUJO_SelfDeactivate ();
  930.  
  931.     return;
  932.   }
  933.  
  934. // regular death
  935.   sound (self, CHAN_VOICE, "dog/ddeath.wav", 1, ATTN_NORM);
  936.   self.solid = SOLID_NOT;
  937.  
  938.   self.deadflag = DEAD_DYING;
  939.  
  940.   if (random() > 0.5)
  941.     CUJO_die1 ();
  942.   else
  943.     CUJO_dieb1 ();
  944. };
  945.  
  946. /*╔═══════════════════════════════════════════════════════════════════════╗
  947.   ║                                                                       ║
  948.   ║ Cujo_checkmelee                                                       ║
  949.   ║                                                                       ║
  950.   ║ Returns TRUE if a melee attack would hit right now                    ║
  951.   ║                                                                       ║
  952.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  953.  
  954. float()    CUJO_CheckMelee =
  955. {
  956.   local   float   dist;
  957.  
  958.   dist = vlen (self.goalentity.origin - self.origin);
  959.  
  960.   if ( ((self.goalentity.classname == "dog_food") && (dist < 100)) ||
  961.       ((self.goalentity.classname != "dog_food") && (dist < 50)) )
  962. //  if (goal_range == RANGE_MELEE)
  963.   {
  964.     self.attack_state = AS_MELEE;
  965.  
  966.     return TRUE;
  967.   }
  968.  
  969.   return FALSE;
  970. };
  971.  
  972. /*╔═══════════════════════════════════════════════════════════════════════╗
  973.   ║                                                                       ║
  974.   ║ Cujo_checkjump                                                        ║
  975.   ║                                                                       ║
  976.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  977.  
  978. float()    CUJO_CheckJump =
  979. {
  980.   local    vector    dist;
  981.   local    float    d;
  982.  
  983.   if (self.origin_z + self.mins_z > self.goalentity.origin_z + self.goalentity.mins_z
  984.   + 0.75 * self.goalentity.size_z)
  985.     return FALSE;
  986.  
  987.   if (self.origin_z + self.maxs_z < self.goalentity.origin_z + self.goalentity.mins_z
  988.   + 0.25 * self.goalentity.size_z)
  989.     return FALSE;
  990.  
  991.   dist = self.goalentity.origin - self.origin;
  992.   dist_z = 0;
  993.  
  994.   d = vlen(dist);
  995.  
  996.   if (d < 80) return FALSE;
  997.  
  998.   if (d > 150) return FALSE;
  999.  
  1000.   return TRUE;
  1001. };
  1002.  
  1003. /*╔═══════════════════════════════════════════════════════════════════════╗
  1004.   ║                                                                       ║
  1005.   ║ CUJO_SpawnMarker                                                      ║
  1006.   ║                                                                       ║
  1007.   ║ For debugging:  spawns a temporary marker at the passed location      ║
  1008.   ║                                                                       ║
  1009.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1010.  
  1011. void (vector org, string mdl_name) CUJO_SpawnMarker =
  1012. {
  1013.   local   entity  marker;
  1014.  
  1015.   marker = spawn ();
  1016.  
  1017.   marker.solid = SOLID_NOT;
  1018.   marker.movetype = MOVETYPE_NONE;
  1019.   marker.think = SUB_Remove;
  1020.   marker.nextthink = time + 10;
  1021.   marker.takedamage = DAMAGE_NO;
  1022.   marker.classname = "debug_marker";
  1023.  
  1024.   setsize (marker, '0 0 0', '0 0 0');
  1025.   setorigin (marker, org);
  1026.   setmodel (marker, mdl_name);
  1027. };
  1028.  
  1029. /*╔═══════════════════════════════════════════════════════════════════════╗
  1030.   ║                                                                       ║
  1031.   ║ CUJO_FindLedge                                                        ║
  1032.   ║                                                                       ║
  1033.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1034.  
  1035. float (float start_dist) CUJO_FindLedge =
  1036.  
  1037. {
  1038.   local   float   i;
  1039.   local   vector  org, end, above_org;
  1040.   local   float   highest, pc;
  1041.   local   float   found_ledge;
  1042.   local   float   ledge_height;
  1043.  
  1044.   local   float   max_mul;
  1045.  
  1046.   if (start_dist > 440)
  1047.     return 0;
  1048.  
  1049.   makevectors (self.angles);
  1050.   // start origin is 48 units above Cujo's origin
  1051.   above_org = self.origin + (v_up * 48);
  1052.  
  1053.   ledge_height = 0.096;
  1054.   found_ledge = FALSE;
  1055.   i = start_dist + 30;
  1056.  
  1057.   while ((i <= 440) && (!found_ledge))
  1058.   {
  1059.     // check the four corners of a 30x30 unit square i units in front of cujo
  1060.     // upper right corner
  1061.     org = above_org + (v_forward * (i + 30)) + (v_right * 15);
  1062.     end = org - (v_up * 1000);
  1063.     traceline (org, end, FALSE, self);
  1064.     highest = trace_fraction;
  1065.  
  1066.     // upper left corner
  1067.     if (trace_fraction <= ledge_height)
  1068.     {
  1069.       org = above_org + (v_forward * (i + 30)) - (v_right * 15);
  1070.       end = org - (v_up * 1000);
  1071.       traceline (org, end, FALSE, self);
  1072.       if (trace_fraction < highest) highest = trace_fraction;
  1073.  
  1074.       if (trace_fraction <= ledge_height)
  1075.       {
  1076.         // lower right corner
  1077.         org = above_org + (v_forward * i) + (v_right * 15);
  1078.         end = org - (v_up * 1000);
  1079.         traceline (org, end, FALSE, self);
  1080.         if (trace_fraction < highest) highest = trace_fraction;
  1081.  
  1082.         if (trace_fraction <= ledge_height)
  1083.         {
  1084.           // lower corner
  1085.           org = above_org + (v_forward * i) - (v_right * 15);
  1086.           end = org - (v_up * 1000);
  1087.           traceline (org, end, FALSE, self);
  1088.           if (trace_fraction < highest) highest = trace_fraction;
  1089.  
  1090.           if (trace_fraction <= ledge_height)
  1091.           {
  1092.             // center
  1093.             org = above_org + (v_forward * (i + 15));
  1094.             end = org - (v_up * 1000);
  1095.             traceline (org, end, FALSE, self);
  1096.             if (trace_fraction < highest) highest = trace_fraction;
  1097.  
  1098.             if (trace_fraction <= ledge_height)
  1099.             {
  1100.               // half way between center and left back corner
  1101.               org = above_org + (v_forward * (i + 7.5)) - (v_right * 7.5);
  1102.               end = org - (v_up * 1000);
  1103.               traceline (org, end, FALSE, self);
  1104.               if (trace_fraction < highest) highest = trace_fraction;
  1105.  
  1106.               if (trace_fraction <= ledge_height)
  1107.               {
  1108.                 // half way between center and right back corner
  1109.                 org = above_org + (v_forward * (i + 7.5)) + (v_right * 7.5);
  1110.                 end = org - (v_up * 1000);
  1111.                 traceline (org, end, FALSE, self);
  1112.                 if (trace_fraction < highest) highest = trace_fraction;
  1113.  
  1114.                 if (trace_fraction <= ledge_height) found_ledge = TRUE;
  1115.               }
  1116.             }
  1117.           }
  1118.         }
  1119.       }
  1120.     }
  1121.     i = i + 30;
  1122.   }
  1123.  
  1124.   if (found_ledge)
  1125.   {
  1126.     //FIXME: predict velocities based on server gravity
  1127.  
  1128.     max_mul = 2.2;
  1129.  
  1130.     highest = highest * 1000;
  1131.     // is ledge above Cujo's origin?
  1132.     if (highest < 48)
  1133.     {
  1134.       self.upward_jump_vel = 270;
  1135.       self.forward_jump_vel = i * 1.5 + 50;
  1136.     }
  1137.     else
  1138.     {
  1139.       self.upward_jump_vel = 270;
  1140.       self.forward_jump_vel = i * 1.5 + 50;
  1141.       return i;
  1142.     }
  1143.   }
  1144.   else
  1145.     return 0;
  1146. };
  1147.  
  1148. /*╔═══════════════════════════════════════════════════════════════════════╗
  1149.   ║                                                                       ║
  1150.   ║ CUJO_JumpObstructed                                                   ║
  1151.   ║                                                                       ║
  1152.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1153.  
  1154. float (float dist) CUJO_JumpObstructed =
  1155.  
  1156. {
  1157.   local   vector  org, end;
  1158.   local   float   pc;
  1159.  
  1160.   // CALL MAKEVECTORS BEFORE CALLING THIS ROUTINE!!!!
  1161. //  makevectors (self.angles);
  1162.   org = self.origin;
  1163.   end = self.origin + (v_forward * dist);
  1164.  
  1165.   traceline (org, end, FALSE, self);
  1166.  
  1167.   if (trace_fraction < 1.0)
  1168.   {
  1169.     CUJO_allstop ();
  1170.     return TRUE;
  1171.   }
  1172.   else
  1173.     return FALSE;
  1174. };
  1175. /*
  1176. float (float dist) CUJO_JumpObstructed =
  1177.  
  1178. {
  1179.   local   vector  org, end;
  1180.   local   float   pc;
  1181.  
  1182.   makevectors (self.angles);
  1183.   org = self.origin;
  1184.   end = self.origin + (v_forward * dist);
  1185.  
  1186.   traceline (org, end, FALSE, self);
  1187.   pc = pointcontents (trace_endpos);
  1188.  
  1189.   if ((trace_fraction < 1.0) || (pc == CONTENT_SOLID))
  1190.     return TRUE;
  1191.   else
  1192.     return FALSE;
  1193. };
  1194. */
  1195.  
  1196. /*╔═══════════════════════════════════════════════════════════════════════╗
  1197.   ║                                                                       ║
  1198.   ║ CUJO_JumpAI                                                           ║
  1199.   ║                                                                       ║
  1200.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1201.  
  1202. float () CUJO_JumpAI =
  1203. {
  1204.   local   vector  dir, org, end, yaw;
  1205.   local   float   dist, pc, i, yaw_add;
  1206.  
  1207.   if (!(self.flags & FL_ONGROUND)) return FALSE;
  1208.  
  1209.   // get vectors!
  1210.   makevectors (self.angles);
  1211.  
  1212.   // check to make sure there's no wall in front, or we could be
  1213.   // tracing for a jump outside the map!
  1214.   traceline (self.origin, self.origin + v_forward * 32, TRUE, self);
  1215.   if (trace_fraction != 1.0)
  1216.     return FALSE;
  1217.  
  1218.   // make sure there's not a small barrier at the feet preventing a jump from going
  1219.   // forward
  1220.   org = self.origin + v_forward * 32;
  1221.   traceline (org, org - '0 0 24', TRUE, self);
  1222.   if (trace_fraction != 1.0) return FALSE;
  1223.  
  1224.   org = self.origin;
  1225.   end = self.goalentity.origin;
  1226.  
  1227.   if (self.dmg_inflictor.classname == "fire")
  1228.   {
  1229.     i = 32;
  1230.     dist = pc = 0;
  1231.     while ((i < 256) && (dist == 0) && (pc != CONTENT_WATER) && (pc != CONTENT_SLIME))
  1232.     {
  1233.       dist = CUJO_CheckWater (i);
  1234.       traceline (self.origin + v_forward * i, self.origin + v_forward * i - '0 0 1000', TRUE, self);
  1235.       pc = pointcontents (trace_endpos + '0 0 2');
  1236.       if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME))
  1237.         dist = i;
  1238.  
  1239.       i = i + 32;
  1240.     }
  1241.  
  1242.     if (!dist)
  1243.     {
  1244.       yaw_add = 22.5;
  1245.       pc = CONTENT_SOLID;
  1246.       i = 0;
  1247.  
  1248.       while ((i < 16) && (dist == 0))
  1249.       {
  1250.         traceline (self.origin + v_forward * 128, self.origin + v_forward * 128 - '0 0 1000', TRUE, self);
  1251.         pc = pointcontents (trace_endpos + '0 0 2');
  1252.         if ((pc == CONTENT_WATER) || (pc == CONTENT_SLIME))
  1253.         {
  1254.           dist = 128;
  1255.           yaw = vectoangles (v_forward);
  1256.           self.ZEUS_stuck = TRUE;
  1257.           self.ZEUS_stuck_dir = 0;
  1258.           self.angles_y = yaw_y;
  1259.           self.ideal_yaw = yaw_y;
  1260.         }
  1261.         else
  1262.         {
  1263.           yaw = vectoangles (v_forward);
  1264.           yaw_y = yaw_y + yaw_add;
  1265.           makevectors (yaw);
  1266.         }
  1267.  
  1268.         i = i + 1;
  1269.       }
  1270.     }
  1271.  
  1272.     if (dist)
  1273.     {
  1274.       self.upward_jump_vel = 270;
  1275.       end = self.goalentity.origin - self.origin;
  1276.       end_z = 0;
  1277.       dist = vlen (end);
  1278. //      if (dist > 300) dist = 300;
  1279.       self.forward_jump_vel = dist * 1.6 + 50;
  1280.  
  1281.       if (self.deadflag == DEAD_NO) CUJO_leap1 ();
  1282.  
  1283.       return TRUE;
  1284.     }
  1285.   }
  1286.   else if (org_z - end_z > 24)
  1287.   {
  1288.     dist = CUJO_CheckWater (32);
  1289.     if (!dist)
  1290.       dist = CUJO_CheckGap (32);
  1291.  
  1292.     pc = pointcontents (self.goalentity.origin);
  1293.  
  1294.     if ((dist) && (pc != CONTENT_LAVA) && (pc != CONTENT_SLIME))
  1295.     {
  1296.       self.upward_jump_vel = 270;
  1297.       end = self.goalentity.origin - self.origin;
  1298.       end_z = 0;
  1299.       dist = vlen (end);
  1300. //      if (dist > 300) dist = 300;
  1301.       self.forward_jump_vel = dist * 1.5 + 100;
  1302.  
  1303.       if (self.deadflag == DEAD_NO) CUJO_leap1 ();
  1304.  
  1305.       return TRUE;
  1306.     }
  1307.   }
  1308. /*
  1309.   else
  1310.   {
  1311.     // turn ZEUS to _exactly_ the direction of his goal entity
  1312.     dir = self.goalentity.origin - self.origin;
  1313.     dir = normalize (dir);
  1314.  
  1315.     dist = CUJO_CheckWater (64);
  1316.  
  1317.     if (dist <= 0)
  1318.     {
  1319.         dist = CUJO_CheckGap (64);
  1320.     }
  1321.  
  1322.     if ((dist > 0) && (dist < 150) && (vlen (self.goalentity.origin - self.origin) >= dist))
  1323.     {
  1324.       dist = CUJO_FindLedge (64);
  1325.  
  1326.       if ((!CUJO_JumpObstructed (dist)) && (dist > 0) && (dist < 320))
  1327.       {
  1328.         if (self.deadflag == DEAD_NO) CUJO_leap1 ();
  1329.  
  1330.         return TRUE;
  1331.       }
  1332.     }
  1333.   }
  1334. */
  1335.   return FALSE;
  1336. };
  1337.  
  1338. /*╔═══════════════════════════════════════════════════════════════════════╗
  1339.   ║                                                                       ║
  1340.   ║ Cujo_checkattack                                                      ║
  1341.   ║                                                                       ║
  1342.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1343.  
  1344. // this checks the attack against self.goalentity not self.enemy!
  1345.  
  1346. float()    CUJO_CheckAttack =
  1347. {
  1348.   local    vector    vec;
  1349.  
  1350.   if ((((self.goalentity == self.owner) || (self.goalentity.classname == "monster_zombie"))
  1351.       || (self.Cujo_attack == FALSE)) && (self.goalentity.classname != "dog_food"))
  1352.   {
  1353.     CUJO_ResetGoalEntity ();
  1354.  
  1355.     self.think = self.th_stand;
  1356.     self.nextthink = time + nextthinktime;
  1357.  
  1358.     return FALSE;
  1359.   }
  1360.  
  1361.   if (CUJO_CheckMelee ())
  1362.   {
  1363.     self.attack_state = AS_MELEE;
  1364.     return TRUE;
  1365.   }
  1366.  
  1367.   if (self.goalentity.classname != "dog_food")
  1368.   {
  1369.     if (CUJO_CheckJump ())
  1370.     {
  1371.       self.attack_state = AS_MISSILE;
  1372.       return TRUE;
  1373.     }
  1374.   }
  1375.  
  1376.   return FALSE;
  1377.  
  1378. };
  1379.  
  1380. /*╔═══════════════════════════════════════════════════════════════════════╗
  1381.   ║                                                                       ║
  1382.   ║ CUJO_ai_run_melee                                                     ║
  1383.   ║                                                                       ║
  1384.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1385.  
  1386. void() CUJO_ai_run_melee =
  1387. {
  1388.   CUJO_ai_face ();
  1389.  
  1390.   if (FacingIdeal())
  1391.   {
  1392.     self.th_melee ();
  1393.     self.attack_state = AS_STRAIGHT;
  1394.   }
  1395. };
  1396.  
  1397. /*╔═══════════════════════════════════════════════════════════════════════╗
  1398.   ║                                                                       ║
  1399.   ║ CUJO_ai_run_missile                                                   ║
  1400.   ║                                                                       ║
  1401.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1402.  
  1403. void() CUJO_ai_run_missile =
  1404. {
  1405.   CUJO_ai_face ();
  1406.  
  1407.   if (FacingIdeal())
  1408.   {
  1409.     self.upward_jump_vel = 270;
  1410.     self.forward_jump_vel = 360;
  1411.     self.th_missile ();
  1412.     self.attack_state = AS_STRAIGHT;
  1413.   }
  1414. };
  1415.  
  1416. /*
  1417. ======================
  1418. CUJO_CheckWaterJump
  1419.  
  1420. Total hack, I'm tired
  1421. ======================
  1422. */
  1423.  
  1424. void() CUJO_CheckWaterJump =
  1425. {
  1426.   local   vector  start, end;
  1427.   local   vector  dir, org;
  1428.   local   float   pc;
  1429.  
  1430.   //determine if there is an open space at head height
  1431.   org = self.origin + v_up * 56;
  1432.   traceline (org, org + v_forward * 32, TRUE, self);
  1433.   if (trace_fraction < 1.0)
  1434.     return;
  1435.  
  1436.   // use trace_line to find obstacles on floor
  1437.   org = self.origin + v_forward * 32 + v_up * 32;
  1438.   traceline (org, org - v_up * 32, TRUE, self);
  1439.  
  1440.   // was 0.8
  1441.   if ((trace_fraction < 1.0))
  1442.   {
  1443.     self.velocity_z = 150 + (1.0 - trace_fraction) * 300;
  1444.     if (self.flags & FL_ONGROUND) setorigin (self, self.origin + '0 0 1');
  1445.     self.flags = self.flags | FL_WATERJUMP;
  1446.   }
  1447. };
  1448.  
  1449. /*
  1450. void() CUJO_CheckWaterJump =
  1451. {
  1452.   local   vector  start, end;
  1453.   local   vector  dir, org;
  1454.   local   float   pc;
  1455.  
  1456.   CUJO_ai_face ();
  1457.   makevectors (self.angles);
  1458.  
  1459.   // use trace_line to find obstacles on floor
  1460.   org = self.origin + v_forward * 32 + v_up * 64;
  1461.   pc = pointcontents (org);
  1462.   traceline (org, org - '0 0 64', TRUE, self);
  1463.  
  1464.   // was 0.8
  1465.   if ((trace_fraction < 1.0) & (pc == CONTENT_EMPTY))
  1466.   {
  1467.     self.velocity_z = 150 + (1.0 - trace_fraction) * 300;
  1468.     self.flags = self.flags | FL_WATERJUMP;
  1469.   }
  1470. };
  1471. */
  1472. /*
  1473. ======================
  1474. CUJO_ai_swim
  1475. ======================
  1476. */
  1477.  
  1478. void () CUJO_ai_swim =
  1479. {
  1480.   local   vector  org, end, dir, ang;
  1481.   local   float   dist;
  1482.  
  1483. //  CUJO_Msgln (ftos (CUJO_WaterLevel (self)));
  1484.   CUJO_ai_face ();
  1485.  
  1486.   if (self.watertype == CONTENT_LAVA)
  1487.     {    // do damage
  1488.         if (self.dmgtime < time)
  1489.         {    
  1490.             self.dmgtime = time + 0.2;
  1491.             T_Damage (self, world, world, 10*self.waterlevel);
  1492.         }
  1493.     }
  1494.     else if (self.watertype == CONTENT_SLIME)
  1495.     {    // do damage
  1496.         self.dmgtime = time + 1;
  1497.         T_Damage (self, world, world, 4*self.waterlevel);
  1498.     }
  1499.  
  1500.   if (self.flags & FL_ONGROUND)
  1501.     self.flags = self.flags - FL_ONGROUND;
  1502.  
  1503.   org = self.origin;
  1504.   end = self.goalentity.origin;
  1505.   dir = normalize (end - org);
  1506.   ang = vectoangles (dir);
  1507.   makevectors (ang);
  1508.  
  1509.   if ((self.goalentity != world))
  1510.   {
  1511.     self.velocity = dir * 320;
  1512.   }
  1513.   else
  1514.   {
  1515.    // go for air
  1516.    CUJO_Msgln ("Going for surface.");
  1517.   }
  1518.  
  1519.   CUJO_CheckWaterJump ();
  1520. };
  1521.  
  1522. /*╔═══════════════════════════════════════════════════════════════════════╗
  1523.   ║                                                                       ║
  1524.   ║ Cujo_ai_run                                                           ║
  1525.   ║                                                                       ║
  1526.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1527.  
  1528. void(float dist) CUJO_ai_run =
  1529. {
  1530.   local    vector    delta;
  1531.   local    float    axis;
  1532.   local    float    direct, ang_rint, ang_floor, ang_ceil, f_temp;
  1533.   local string  str_temp;
  1534.  
  1535. //  sprint (self.owner, "-- enter ai_run --\n");
  1536.  
  1537.   movedist = dist;
  1538.  
  1539. //  sprint (self.owner, "Enemy is ");
  1540. //  sprint (self.owner, self.enemy.classname);
  1541. //  sprint (self.owner, "\n");
  1542. //  sprint (self.owner, "Goalentity is ");
  1543. //  sprint (self.owner, self.goalentity.classname);
  1544. //  sprint (self.owner, "\n");
  1545.  
  1546.   if ((self.goalentity.classname != "dog_food") && (self.goalentity.health <= 0))
  1547.   {
  1548.     CUJO_ResetGoalEntity ();
  1549.  
  1550.     //sprint (self.owner, "CUJO_ai_run: reset goalentity\n");
  1551.  
  1552.     if (self.oldenemy.health > 0)
  1553.     {
  1554.       self.enemy = self.oldenemy;
  1555.       self.goalentity = self.oldenemy;
  1556.  
  1557.       CUJO_HuntTarget ();
  1558.     }
  1559.     else
  1560.     {
  1561.       if (CUJO_FindTarget()) return;
  1562.       else
  1563.       {
  1564.         CUJO_ResetGoalEntity ();
  1565.  
  1566.         //sprint (self.owner, "CUJO_ai_run #2: reset goalentity\n");
  1567.  
  1568.         self.th_walk ();
  1569.  
  1570. //        sprint (self.owner, "-- Left ai_run at return 1 --\n");
  1571.         return;
  1572.       }
  1573.     }
  1574.  
  1575. //    sprint (self.owner, "-- Left ai_run at return 2 --\n");
  1576.     return;
  1577.   }
  1578.  
  1579.   if (self.goalentity.classname != "dog_food")
  1580.     self.show_hostile = time + 1;        // wake up other monsters
  1581.  
  1582.   goal_vis = visible(self.goalentity);
  1583.   if (goal_vis) self.search_time = time + 5;
  1584.  
  1585.   goal_infront = infront (self.goalentity);
  1586.   goal_range = range (self.goalentity);
  1587.   goal_yaw = vectoyaw (self.goalentity.origin - self.origin);
  1588.  
  1589.   if ((self.attack_state == AS_MISSILE) && (self.goalentity.classname != "dog_food"))
  1590.   {
  1591.     CUJO_ai_run_missile ();
  1592.     return;
  1593.   }
  1594.   if (self.attack_state == AS_MELEE)
  1595.   {
  1596.  
  1597. // for debugging
  1598. //    f_temp = vlen (self.origin - self.goalentity.origin);
  1599. //    str_temp = ftos (f_temp);
  1600.  
  1601. //    sprint (self.owner, "Cujo is ");
  1602. //    sprint (self.owner, str_temp);
  1603. //    sprint (self.owner, " units from ");
  1604. //    sprint (self.owner, self.goalentity.classname);
  1605. //    sprint (self.owner, ".\n");
  1606.  
  1607.     CUJO_ai_run_melee ();
  1608.  
  1609.     return;
  1610.   }
  1611.  
  1612.   if (CUJO_JumpAI ()) return;           // needs to jump to goal
  1613.   if (CUJO_CheckAttack ()) return;    // beginning an attack
  1614.  
  1615.   if (CUJO_WaterLevel (self) > 1)
  1616.     CUJO_ai_swim ();
  1617.   else
  1618.     movetogoal (dist);
  1619. //    CUJO_movetogoal (dist);        // done in C code...
  1620.  
  1621. //  sprint (self.owner, "-- exit ai_run --\n");
  1622. };
  1623.  
  1624. /*╔═══════════════════════════════════════════════════════════════════════╗
  1625.   ║                                                                       ║
  1626.   ║ Cujo_SightSound                                                       ║
  1627.   ║                                                                       ║
  1628.   ║ dog stands in place until target acquired or paustime is exceeded     ║
  1629.   ║                                                                       ║
  1630.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1631.  
  1632. void() CUJO_SightSound =
  1633. {
  1634.   sound (self, CHAN_VOICE, "dog/dsight.wav", 1, ATTN_NORM);
  1635. };
  1636.  
  1637. /*╔═══════════════════════════════════════════════════════════════════════╗
  1638.   ║                                                                       ║
  1639.   ║ Cujo_HuntTarget                                                       ║
  1640.   ║                                                                       ║
  1641.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1642.  
  1643. void() CUJO_HuntTarget =
  1644. {
  1645. // for debugging
  1646.   //sprint (self.owner, "CUJO_HuntTarget: ");
  1647.   //sprint (self.owner, self.goalentity.classname);
  1648.   //sprint (self.owner, "\n");
  1649.  
  1650.   self.think = self.th_run;
  1651.   self.ideal_yaw = vectoyaw(self.goalentity.origin - self.origin);
  1652.   self.nextthink = time + nextthinktime;
  1653.  
  1654.   SUB_AttackFinished (0.1);
  1655. };
  1656.  
  1657. /*╔═══════════════════════════════════════════════════════════════════════╗
  1658.   ║                                                                       ║
  1659.   ║ Cujo_FoundTarget                                                      ║
  1660.   ║                                                                       ║
  1661.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1662.  
  1663. void() CUJO_FoundTarget =
  1664. {
  1665.   local float   f_dist;
  1666.   local string  s_dist;
  1667.   local float   r;
  1668.  
  1669.   f_dist = vlen (self.goalentity.origin - self.origin);
  1670.   s_dist = ftos (f_dist);
  1671.  
  1672.   self.show_hostile = time + 1;        // wake up other monsters
  1673.  
  1674.   r = random ();
  1675.  
  1676.   if (r < 0.5)
  1677.     sprint (self.owner, "rrrrRROOF!\n");
  1678.   else
  1679.     sprint (self.owner, "grrrrllll...\n");
  1680.  
  1681. // for debugging
  1682.   //sprint (self.owner, "Cujo seeking target");
  1683.   //sprint (self.owner, self.goalentity.classname);
  1684.   //sprint (self.owner, "\n");
  1685.  
  1686.   CUJO_SightSound ();
  1687.  
  1688.   CUJO_HuntTarget ();
  1689. };
  1690.  
  1691. /*╔═══════════════════════════════════════════════════════════════════════╗
  1692.   ║                                                                       ║
  1693.   ║ Cujo_FindTarget                                                       ║
  1694.   ║                                                                       ║
  1695.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1696.  
  1697. float() CUJO_FindTarget =
  1698. {
  1699.   local entity   head, selected;
  1700.   local entity   food;
  1701.   local float    dist, lastd;
  1702.   local float    food_dist, food_lastd;
  1703.  
  1704.   // check for any enemies visible to Cujo
  1705.   dist = 600;
  1706.   food_dist = 600;
  1707.   selected = world;
  1708.   food = world;
  1709.  
  1710.   head = findradius(self.origin, dist);
  1711.  
  1712.   // CUJO 1.3 - trace_plane_dist was always equal to zero for some
  1713.   // unknown reason, so the code that used it was replaced
  1714.   while(head)
  1715.   {
  1716.     // search for food
  1717.     if ((head.classname == "dog_food") && (visible (head)))
  1718.     {
  1719.       if ((head != self) && (head != self.owner))
  1720.       {
  1721.         food_lastd = vlen (self.origin - head.origin);
  1722.         if (food_lastd < food_dist)
  1723.         // for efficiency's sake, go after the closest gib
  1724.         {
  1725.           food = head;
  1726.           food_dist = food_lastd;
  1727.         }
  1728.       }
  1729.     }
  1730.  
  1731.     // search for enemy
  1732.     if(!(head.flags & FL_NOTARGET) && ((head.flags & FL_CLIENT) || (head.flags & FL_MONSTER) ||
  1733.         (head.classname == "cujo")))
  1734.       if (((teamplay) &&  (head.team != self.owner.team)) || (!teamplay))
  1735.         if ((head.health > 0) && (head != self) && (head != self.owner))
  1736.           if ((visible(head)) && (head.classname != "monster_zombie"))
  1737.           {
  1738.             lastd = vlen (self.origin - head.origin);
  1739.             if (lastd < dist)
  1740.             {
  1741.               selected = head;
  1742.               dist = lastd;
  1743.             }
  1744.           }
  1745.     head = head.chain;
  1746.   }
  1747.  
  1748.   self.enemy = selected;
  1749.  
  1750.   if ((food != world) && (food != self.owner) && (self.Cujo_attack)
  1751.     && ((self.health < 50) || ((deathmatch) && (self.health < 25))))
  1752.   {
  1753.     self.goalentity = food;
  1754.     self.enemy = food;
  1755.  
  1756.     CUJO_FoundTarget ();
  1757.  
  1758.     return TRUE;
  1759.   }
  1760.   else if ((self.enemy == world) || (self.enemy == self.owner))
  1761.   {
  1762.     if ((food != world) && (food != self.owner) && ((self.health < 91) &&
  1763.         (self.Cujo_attack)))
  1764.     {
  1765.       self.goalentity = food;
  1766.       self.enemy = food;
  1767.  
  1768.       CUJO_FoundTarget ();
  1769.  
  1770.       return TRUE;
  1771.     }
  1772.  
  1773.     return FALSE;
  1774.   }
  1775.   else  // an enemy target has been sighted
  1776.   {
  1777.     if (!(self.Cujo_attack) || (self.enemy.classname == "dog_food"))
  1778.     // if not supposed to attack, just growl
  1779.     {
  1780.       if (random () < 0.10)
  1781.       {
  1782.         // make Cujo bark or growl
  1783.         if (random () < 0.5)
  1784.           sound (self, CHAN_VOICE, "dog/dsight.wav", 1, ATTN_NORM);
  1785.         else
  1786.           sound (self, CHAN_VOICE, "dog/dattack1.wav", 1, ATTN_NORM);
  1787.       }
  1788.  
  1789.       CUJO_ResetGoalEntity ();
  1790.  
  1791.       return FALSE;
  1792.     }
  1793.  
  1794.     // if Cujo is staying, make him attack!
  1795.     self.Cujo_stay = FALSE;
  1796.  
  1797.     self.goalentity = self.enemy;
  1798.  
  1799.     CUJO_FoundTarget();
  1800.  
  1801.     return TRUE;
  1802.   }
  1803. };
  1804.  
  1805. /*╔═══════════════════════════════════════════════════════════════════════╗
  1806.   ║                                                                       ║
  1807.   ║ CUJO_ai_face                                                          ║
  1808.   ║                                                                       ║
  1809.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1810.  
  1811. void() CUJO_ai_face =
  1812. {
  1813.   if (self.ZEUS_stuck)
  1814.     self.ideal_yaw = self.ideal_yaw + self.ZEUS_stuck_dir;
  1815.   else
  1816.     self.ideal_yaw = vectoyaw (self.goalentity.origin - self.origin);
  1817.  
  1818.   CUJO_ChangeYaw ();
  1819. };
  1820.  
  1821. /*╔═══════════════════════════════════════════════════════════════════════╗
  1822.   ║                                                                       ║
  1823.   ║ CUJO_ai_charge                                                        ║
  1824.   ║                                                                       ║
  1825.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1826.  
  1827. void (float d) CUJO_ai_charge =
  1828. {
  1829.   CUJO_ai_face ();
  1830.  
  1831. movetogoal (d);        
  1832. //  CUJO_movetogoal (d);        // done in C code...
  1833. };
  1834.  
  1835. /*╔═══════════════════════════════════════════════════════════════════════╗
  1836.   ║                                                                       ║
  1837.   ║ Cujo_ai_stand                                                         ║
  1838.   ║                                                                       ║
  1839.   ║ dog stands in place until target acquired or paustime is exceeded     ║
  1840.   ║                                                                       ║
  1841.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1842.  
  1843. void() CUJO_ai_stand =
  1844. {
  1845.   if (CUJO_FindTarget ()) return;
  1846.  
  1847.   if (vlen (self.origin - self.owner.origin) > teleport_dist)
  1848.   {
  1849.     CUJO_TeleportToOwner ();
  1850.   }
  1851.   else if (vlen (self.origin - self.owner.origin) > 100)
  1852.   {
  1853.     // if Cujo is staying, then he shouldn't run in place!
  1854.     if (self.Cujo_stay == TRUE) return;
  1855.  
  1856.     CUJO_follow1 ();
  1857.  
  1858.     return;
  1859.   }
  1860.  
  1861.   if (CUJO_JumpAI ()) return;
  1862. };
  1863.  
  1864. /*╔═══════════════════════════════════════════════════════════════════════╗
  1865.   ║                                                                       ║
  1866.   ║ Cujo_ai_walk                                                          ║
  1867.   ║                                                                       ║
  1868.   ║ Cujo is walking and searching for targets                             ║
  1869.   ║                                                                       ║
  1870.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1871.  
  1872. void(float dist) CUJO_ai_walk =
  1873. {
  1874.   if (CUJO_FindTarget ()) return;
  1875.  
  1876.   if (vlen (self.origin - self.owner.origin) > 100)
  1877.   {
  1878.     CUJO_follow1();
  1879.  
  1880.     return;
  1881.   }
  1882.   else if (vlen (self.origin - self.owner.origin) > teleport_dist)
  1883.   {
  1884.     CUJO_TeleportToOwner ();
  1885.   }
  1886.  
  1887.   if (CUJO_WaterLevel (self) > 1)
  1888.     CUJO_ai_swim ();
  1889.   else
  1890.   {
  1891.     if (CUJO_JumpAI ()) return;
  1892.  
  1893. movetogoal (dist);
  1894. //    CUJO_movetogoal (dist);  // this is done in C code
  1895.   }
  1896. };
  1897.  
  1898. /*╔═══════════════════════════════════════════════════════════════════════╗
  1899.   ║                                                                       ║
  1900.   ║ Cujo_ai_follow                                                        ║
  1901.   ║                                                                       ║
  1902.   ║ Cujo is following player and searching for targets                    ║
  1903.   ║                                                                       ║
  1904.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1905.  
  1906. void(float dist) CUJO_ai_follow =
  1907. {
  1908.   if (CUJO_FindTarget ()) return;
  1909.  
  1910.   //sprint (self.owner, "Following owner.\n");
  1911.   //sprint (self.owner, "Goalentity is ");
  1912.   //sprint (self.owner, self.goalentity.classname);
  1913.   //sprint (self.owner, "\n");
  1914.   //sprint (self.owner, "Enemy is ");
  1915.   //sprint (self.owner, self.enemy.classname);
  1916.   //sprint (self.owner, "\n");
  1917.   //sprint (self.owner, "Movetarget is ");
  1918.   //sprint (self.owner, self.movetarget.classname);
  1919.   //sprint (self.owner, "\n");
  1920.  
  1921.   // if Cujo is in STAY mode then do not follow the player!
  1922.  
  1923.   if ((vlen (self.origin - self.owner.origin) <= 100) || (self.Cujo_stay))
  1924.   {
  1925.     self.pausetime = time + 2;
  1926.  
  1927.     self.th_stand ();
  1928.  
  1929.     return;
  1930.   }
  1931.   else if ((vlen (self.origin - self.owner.origin) > teleport_dist) &&
  1932.           (!intermission_running))
  1933.   {
  1934.     CUJO_TeleportToOwner ();
  1935.   }
  1936.  
  1937.   if (CUJO_WaterLevel (self) > 1)
  1938.     CUJO_ai_swim ();
  1939.   else
  1940.   {
  1941.     if (CUJO_JumpAI ()) return;
  1942. movetogoal (dist);
  1943. //    CUJO_movetogoal (dist);  // done in C code
  1944.   }
  1945. };
  1946.  
  1947. /*╔═══════════════════════════════════════════════════════════════════════╗
  1948.   ║                                                                       ║
  1949.   ║ Cujo_ai_turn                                                          ║
  1950.   ║                                                                       ║
  1951.   ║ Turn Cujo towards ideal_yaw if no enemies were sighted                ║
  1952.   ║                                                                       ║
  1953.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1954.  
  1955. void() CUJO_ai_turn =
  1956. {
  1957.   if (CUJO_FindTarget ()) return;
  1958.  
  1959.   CUJO_ChangeYaw ();
  1960. };
  1961.  
  1962. /*╔═══════════════════════════════════════════════════════════════════════╗
  1963.   ║                                                                       ║
  1964.   ║ Cujo_CheckRefire                                                      ║
  1965.   ║                                                                       ║
  1966.   ║ Determine if the enemy is still visible for attack                    ║
  1967.   ║                                                                       ║
  1968.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1969.  
  1970. void (void () thinkst) CUJO_CheckRefire =
  1971. {
  1972.   if (!visible (self.goalentity) || (self.goalentity.health <= 0))
  1973.   {
  1974.     CUJO_ResetGoalEntity ();
  1975.  
  1976.     self.think = self.th_stand;
  1977.     self.nextthink = time + nextthinktime;
  1978.  
  1979.     return;
  1980.   }
  1981.  
  1982. //  self.think = thinkst;  // was thinkst
  1983. };
  1984.  
  1985. /*╔═══════════════════════════════════════════════════════════════════════╗
  1986.   ║                                                                       ║
  1987.   ║ CUJO_CorpsePain                                                       ║
  1988.   ║                                                                       ║
  1989.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  1990.  
  1991. void () CUJO_CorpsePain =
  1992. {
  1993.  SpawnMeatSpray (self.origin, crandom() * 100 * v_right);
  1994. };
  1995.  
  1996. /*╔═══════════════════════════════════════════════════════════════════════╗
  1997.   ║                                                                       ║
  1998.   ║ CUJO_CorpseDie;                                                       ║
  1999.   ║                                                                       ║
  2000.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2001.  
  2002. void () CUJO_CorpseDie =
  2003. {
  2004.   local   float   i;
  2005.   local   float   j;
  2006.  
  2007.   sound (self, CHAN_VOICE, "player/udeath.wav", 1, ATTN_NORM);
  2008.  
  2009.   // throw the head -- can be a player head or a cujo head only in this version!
  2010.   ThrowHead (self.weaponmodel, self.health);
  2011.  
  2012.   i = 0;
  2013.  
  2014.   while(i<3)
  2015.   {
  2016.     j = random();
  2017.  
  2018.     if(j > 0.6)
  2019.       ThrowGib ("progs/gib1.mdl", self.health);
  2020.     else if(j > 0.3)
  2021.       ThrowGib ("progs/gib2.mdl", self.health);
  2022.     else
  2023.       ThrowGib ("progs/gib3.mdl", self.health);
  2024.  
  2025.     i = i + 1;
  2026.   }
  2027.  
  2028. // Cujo's head only stays around for 2 minutes...
  2029. //  self.nextthink = time + 120;
  2030. //  self.think = SUB_Remove;
  2031. };
  2032.  
  2033. /*╔═══════════════════════════════════════════════════════════════════════╗
  2034.   ║                                                                       ║
  2035.   ║ Cujo_SelfDeactivate                                                   ║
  2036.   ║                                                                       ║
  2037.   ║ Cujo deactivates himself (when he is killed, basically)               ║
  2038.   ║                                                                       ║
  2039.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2040.  
  2041. void () CUJO_SelfDeactivate =
  2042. {
  2043.   local   entity  temp_self;
  2044.   local   string  deathstr, tempstr;
  2045.  
  2046.   temp_self = self;
  2047.   self = self.owner;
  2048.  
  2049.   if (self.Cujo_view)
  2050.     CUJO_SetPlayerView ();
  2051.  
  2052.   self = temp_self;
  2053.  
  2054.   // create a dummy Cujo body... this alleviates the problem of the
  2055.   // original Cujo entity becoming lost if the player reactivates Cujo
  2056.   // before the body has disappeared
  2057.  
  2058. //  self.classname = "cujo_body";
  2059.  
  2060.   // create a body in the que which is solid
  2061.   self.weaponmodel = "progs/h_cujo.mdl";
  2062.   CopyToBodyQue (self);
  2063.  
  2064.   if (self.enemy == self.owner)
  2065.   {
  2066.     if (self.health < -35)
  2067.     {
  2068.       sprint (self.owner, "You just turned Cujo into puppy chow.\n");
  2069.     }
  2070.     else
  2071.     {
  2072.       sprint (self.owner, "You buried your dog.\n");
  2073.     }
  2074.   }
  2075.   else if (self.enemy.classname == "player")
  2076.   {
  2077.     deathstr = self.enemy.netname;
  2078.     if (self.health < -35)
  2079.     {
  2080.       tempstr = " just chunked your dog.\n";
  2081.     }
  2082.     else
  2083.     {
  2084.       if (random () < 0.5)
  2085.       {
  2086.         tempstr = " taught Cujo to play dead.\n";
  2087.       }
  2088.       else
  2089.       {
  2090.         tempstr = " took Cujo to the pound.\n";
  2091.       }
  2092.     }
  2093.  
  2094.     sprint (self.owner, deathstr);
  2095.     sprint (self.owner, tempstr);
  2096.   }
  2097.   else
  2098.   {
  2099.     if (self.health < -35)
  2100.     {
  2101.       sprint (self.owner, "Cujo is kibbles and bits.\n");
  2102.     }
  2103.     else
  2104.     {
  2105.       sprint (self.owner, "Cujo went to dog heaven.\n");
  2106.     }
  2107.   }
  2108.  
  2109.   self.owner.Cujo_flag = FALSE;
  2110.   remove (self);
  2111. };
  2112.  
  2113. /*╔═══════════════════════════════════════════════════════════════════════╗
  2114.   ║                                                                       ║
  2115.   ║ Cujo_TeleportPos                                                      ║
  2116.   ║                                                                       ║
  2117.   ║ Determines the best position for Cujo to spawn in                     ║
  2118.   ║ This procedure is to be called from Cujo's routines, not the player's ║
  2119.   ║                                                                       ║
  2120.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2121.  
  2122. vector () CUJO_TeleportPos =
  2123. {
  2124.   local vector  org, temp, right, front;
  2125.  
  2126.   org = self.owner.origin;
  2127.  
  2128.   makevectors (self.owner.angles);
  2129.  
  2130.   // make front equal 50 units in front of the player
  2131.   front = 50 * normalize (v_forward);
  2132.   // and right equal fifty units to the right
  2133.   right = 50 * normalize (v_right);
  2134.  
  2135.  
  2136.   // right of player
  2137.  
  2138.   temp = org + right;
  2139.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2140.  
  2141.   // left of player
  2142.  
  2143.   temp = org - right;
  2144.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2145.  
  2146.   // in front of player
  2147.  
  2148.   temp = org + front;
  2149.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2150.  
  2151.   // behind player
  2152.  
  2153.   temp = org - front;
  2154.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2155.  
  2156. };
  2157.  
  2158.  
  2159. /*╔═══════════════════════════════════════════════════════════════════════╗
  2160.   ║                                                                       ║
  2161.   ║ Cujo_TeleportToOwner                                                  ║
  2162.   ║                                                                       ║
  2163.   ║ Cujo teleports to his owner, the player                               ║
  2164.   ║                                                                       ║
  2165.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2166.  
  2167. void () CUJO_TeleportToOwner =
  2168. {
  2169.   local vector org;
  2170.  
  2171.   // exit if in intermission or if Cujo is staying
  2172.   if ((intermission_running) || (self.Cujo_stay)) return;
  2173.  
  2174.   // Check to see if there is room to teleport Cujo here, if not, say so and
  2175.   // exit.
  2176.   org = CUJO_TeleportPos ();
  2177.   if (org == '0 0 0')
  2178.     return;
  2179.  
  2180.   sprint (self.owner, "Whoof!\n");
  2181.   sound (self, CHAN_BODY, "dog/dsight.wav", 1, ATTN_NORM);
  2182.  
  2183.   spawn_tfog (self.origin);
  2184.  
  2185.   self.angles = '0 0 0';
  2186.   self.ideal_yaw = self.angles * '0 1 0';
  2187.   self.pausetime = time + 5;
  2188.   self.nextthink = time + nextthinktime;
  2189.   self.think = self.Cujo.th_stand;
  2190.  
  2191.   setorigin(self, org);
  2192.  
  2193.   spawn_tfog (self.origin);
  2194.   self.nextthink = time + nextthinktime;
  2195.   self.think = self.th_stand;
  2196.  
  2197.   return;
  2198. };
  2199.  
  2200. /*╔═══════════════════════════════════════════════════════════════════════╗
  2201.   ║                                                                       ║
  2202.   ║                 Routines called by player, self=player                ║
  2203.   ║                                                                       ║
  2204.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2205.  
  2206. /*╔═══════════════════════════════════════════════════════════════════════╗
  2207.   ║                                                                       ║
  2208.   ║ Cujo_Precache                                                         ║
  2209.   ║                                                                       ║
  2210.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2211.  
  2212. void () CUJO_Precache =
  2213. {
  2214.   // Precache Cujo's sounds
  2215.   precache_model2 ("progs/h_cujo.mdl");
  2216.   precache_model2 ("progs/cujo.mdl");
  2217.   precache_model2 ("progs/cujarmor.mdl");
  2218.  
  2219.   precache_sound2 ("dog/dattack1.wav");
  2220.   precache_sound2 ("dog/ddeath.wav");
  2221.   precache_sound2 ("dog/dpain1.wav");
  2222.   precache_sound2 ("dog/dsight.wav");
  2223.   precache_sound2 ("dog/idle.wav");
  2224. };
  2225.  
  2226. /*╔═══════════════════════════════════════════════════════════════════════╗
  2227.   ║                                                                       ║
  2228.   ║ Cujo_CheckSpawnPos                                                    ║
  2229.   ║                                                                       ║
  2230.   ║ Returns true if Cujo's bounding box area around SpawnPos is free of   ║
  2231.   ║ solid objects                                                         ║
  2232.   ║                                                                       ║
  2233.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2234.  
  2235. float (vector SpawnPos) CUJO_CheckSpawnPos =
  2236. {
  2237.   local  vector  XVector, YVector, ZPosVec, ZNegVec;
  2238.   local  vector  HalfYVec, HalfXVec, HalfZPosVec, HalfZNegVec;
  2239.   local  float   pc;
  2240.  
  2241.   // These vector offsets match Cujo's current size, but do not match
  2242.   // the exact size of the Dog model (which was too big to follow the
  2243.   // player some places), so the dog may at times look like his head
  2244.   // is in a wall, but he shouldn't get stuck (we hope).
  2245.  
  2246.   XVector = '16 0 0';
  2247.   HalfXVec = '8 0 0';
  2248.  
  2249.   YVector = '0 16 0';
  2250.   HalfYVec = '0 16 0';
  2251.  
  2252.   ZPosVec = '0 0 16';
  2253.   HalfZPosVec = '0 0 8';
  2254.   ZNegVec = '0 0 -24';
  2255.   HalfZNegVec = '0 0 -12';
  2256.  
  2257.   // check the very center of the box, just in case something is floating
  2258.   // there (for example, the moving "key cubes" at the end of e1m2)
  2259.  
  2260.   pc = pointcontents (SpawnPos);
  2261.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2262.  
  2263.   // check half the distance to each face of the box.  Again, this is
  2264.   // for floating or thin objects which might fit in between the face
  2265.   // edge and the center of the face
  2266.  
  2267.   pc = pointcontents (SpawnPos + HalfXVec);
  2268.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2269.  
  2270.   pc = pointcontents (SpawnPos - HalfXVec);
  2271.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2272.  
  2273.   pc = pointcontents (SpawnPos + HalfYVec);
  2274.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2275.  
  2276.   pc = pointcontents (SpawnPos - HalfYVec);
  2277.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2278.  
  2279.   pc = pointcontents (SpawnPos + HalfZPosVec);
  2280.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2281.  
  2282.   pc = pointcontents (SpawnPos + HalfZNegVec);
  2283.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2284.  
  2285.  
  2286.   // this checks the six faces of each side of Cujo's bounding box
  2287.  
  2288.   pc = pointcontents (SpawnPos + XVector);
  2289.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2290.  
  2291.   pc = pointcontents (SpawnPos - XVector);
  2292.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2293.  
  2294.   pc = pointcontents (SpawnPos + YVector);
  2295.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2296.  
  2297.   pc = pointcontents (SpawnPos - YVector);
  2298.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2299.  
  2300.   pc = pointcontents (SpawnPos + ZPosVec);
  2301.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2302.  
  2303.   pc = pointcontents (SpawnPos + ZNegVec);
  2304.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2305.  
  2306.  
  2307.   // Check the eight vertices of Cujo's bounding box
  2308.  
  2309.   pc = pointcontents (SpawnPos + XVector + YVector + ZPosVec);
  2310.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2311.  
  2312.   pc = pointcontents (SpawnPos + XVector + YVector + ZNegVec);
  2313.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2314.  
  2315.   pc = pointcontents (SpawnPos + XVector - YVector + ZPosVec);
  2316.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2317.  
  2318.   pc = pointcontents (SpawnPos + XVector - YVector + ZNegVec);
  2319.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2320.  
  2321.   pc = pointcontents (SpawnPos - XVector + YVector + ZPosVec);
  2322.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2323.  
  2324.   pc = pointcontents (SpawnPos - XVector + YVector + ZNegVec);
  2325.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2326.  
  2327.   pc = pointcontents (SpawnPos - XVector - YVector + ZPosVec);
  2328.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2329.  
  2330.   pc = pointcontents (SpawnPos - XVector - YVector + ZNegVec);
  2331.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2332.  
  2333.  
  2334.   // check the center of each edge of the box for content type
  2335.   // first check the center of the top 4 edges of the bounding box
  2336.  
  2337.   pc = pointcontents (SpawnPos + (YVector + ZPosVec));
  2338.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2339.  
  2340.   pc = pointcontents (SpawnPos - (YVector + ZPosVec));
  2341.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2342.  
  2343.   pc = pointcontents (SpawnPos + (XVector + ZPosVec));
  2344.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2345.  
  2346.   pc = pointcontents (SpawnPos - (XVector + ZPosVec));
  2347.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2348.  
  2349.   // now check the center of the middle 4 edges of the bounding box
  2350.  
  2351.   pc = pointcontents (SpawnPos - (XVector + YVector));
  2352.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2353.  
  2354.   pc = pointcontents (SpawnPos - (XVector - YVector));
  2355.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2356.  
  2357.   pc = pointcontents (SpawnPos + (XVector + YVector));
  2358.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2359.  
  2360.   pc = pointcontents (SpawnPos + (XVector - YVector));
  2361.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2362.  
  2363.   // now check the center of the bottom 4 edges of the bounding box
  2364.  
  2365.   pc = pointcontents (SpawnPos + (YVector + ZNegVec));
  2366.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2367.  
  2368.   pc = pointcontents (SpawnPos - (YVector + ZNegVec));
  2369.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2370.  
  2371.   pc = pointcontents (SpawnPos + (XVector + ZNegVec));
  2372.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2373.  
  2374.   pc = pointcontents (SpawnPos - (XVector + ZNegVec));
  2375.   if (pc != CONTENT_EMPTY && pc != CONTENT_WATER) return FALSE;
  2376.  
  2377.   // if that doesn't check enough spots, then that's just too bad.
  2378.  
  2379.   return TRUE;
  2380. };
  2381.  
  2382. /*╔═══════════════════════════════════════════════════════════════════════╗
  2383.   ║                                                                       ║
  2384.   ║ Cujo_SpawnPos                                                         ║
  2385.   ║                                                                       ║
  2386.   ║ Determines the best position for Cujo to spawn in                     ║
  2387.   ║                                                                       ║
  2388.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2389.  
  2390. vector () CUJO_SpawnPos =
  2391. {
  2392.   local vector  org, temp, right, front;
  2393.  
  2394.   org = self.origin;
  2395.  
  2396.   makevectors (self.angles);
  2397.  
  2398.   // make front equal 50 units in front of the player
  2399.   front = 50 * normalize (v_forward);
  2400.   // and right equal fifty units to the right
  2401.   right = 50 * normalize (v_right);
  2402.  
  2403.  
  2404.   // right of player
  2405.  
  2406.   temp = org + right;
  2407.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2408.  
  2409.   // left of player
  2410.  
  2411.   temp = org - right;
  2412.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2413.  
  2414.   // in front of player
  2415.  
  2416.   temp = org + front;
  2417.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2418.  
  2419.   // behind player
  2420.  
  2421.   temp = org - front;
  2422.   if (CUJO_CheckSpawnPos (temp)) return temp;
  2423.  
  2424. };
  2425.  
  2426.  
  2427. /*╔═══════════════════════════════════════════════════════════════════════╗
  2428.   ║                                                                       ║
  2429.   ║ Cujo_Activate                                                         ║
  2430.   ║                                                                       ║
  2431.   ║ Called by player, self = player                                       ║
  2432.   ║                                                                       ║
  2433.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2434.  
  2435. void () CUJO_Activate =
  2436. {
  2437.   local entity  dogbot;
  2438.   local vector  org;
  2439.   local float   bit;
  2440.  
  2441.   // Check to see if there is room to spawn Cujo here, if not, say so and
  2442.   // exit.
  2443.  
  2444.   if (deathmatch)
  2445.     org = cujo_org + '0 0 24';
  2446.   else
  2447.   {
  2448.     org = CUJO_SpawnPos ();
  2449.     if (org == '0 0 0')
  2450.     {
  2451.       sprint (self, "There is no room for Cujo here.\n");
  2452.       return;
  2453.     }
  2454.   }
  2455.  
  2456.   // Spawn Cujo
  2457.  
  2458.   dogbot = spawn();
  2459.   dogbot.solid = SOLID_BBOX;
  2460.   dogbot.movetype = MOVETYPE_STEP;
  2461.   dogbot.CUJO_verbose = TRUE;
  2462.  
  2463.   dogbot.angles = self.angles;
  2464.   dogbot.classname = "cujo";
  2465.   dogbot.owner=self;
  2466.   self.Cujo=dogbot;
  2467.   self.Cujo_flag = TRUE;
  2468.   self.Cujo_view = FALSE;
  2469.   self.Cujo.Cujo_attack = TRUE;
  2470.   self.Cujo.Cujo_stay = FALSE;
  2471.  
  2472.   dogbot.takedamage = DAMAGE_AIM;
  2473.   dogbot.goalentity = self;
  2474.   dogbot.movetarget = self;
  2475.   dogbot.pausetime = time + 5;
  2476.   dogbot.ideal_yaw = dogbot.angles * '0 1 0';
  2477.   dogbot.yaw_speed = 90; // was 30 for a normal dog, looks really odd from Cujo's view
  2478.  
  2479.   // lowered Cujo's view offset to 15 above the center of it's bounding box
  2480.   // (I have no idea if this is exactly right...)
  2481.  
  2482.   dogbot.view_ofs = '0 0 15';
  2483.  
  2484.   // this dog has some badass armor
  2485.  
  2486.   dogbot.health = 100;
  2487.   bit = IT_ARMOR3;
  2488.   dogbot.armortype = 0.8;
  2489.   dogbot.armorvalue = 50;
  2490.   dogbot.items = other.items - (other.items & (IT_ARMOR1 | IT_ARMOR2 | IT_ARMOR3)) + bit;
  2491.  
  2492.   // Set to automatic mode sequences at startup
  2493.  
  2494.   dogbot.th_stand = CUJO_stand1;
  2495.   dogbot.th_walk = CUJO_walk1;
  2496.   dogbot.th_run = CUJO_run1;
  2497.   dogbot.th_pain = CUJO_pain;
  2498.   dogbot.th_die = CUJO_die;
  2499.   dogbot.th_melee = CUJO_atta1;
  2500.   dogbot.th_missile = CUJO_leap1;
  2501.   dogbot.team = self.team;
  2502.   dogbot.deadflag = DEAD_NO;
  2503.  
  2504.   setmodel (dogbot, "progs/cujo.mdl");
  2505.  
  2506.   // Cujo's size was made the same as the player, (except for his height)
  2507.   // because he was having trouble getting though some narrow doorways
  2508.   // and halls.
  2509.  
  2510.   setsize (dogbot, '-16 -16 -24', '16 16 16');
  2511.   setorigin(dogbot, org);
  2512.  
  2513.   spawn_tfog (dogbot.origin);
  2514.  
  2515.   sound (self, CHAN_BODY, "dog/dattack1.wav", 1, ATTN_NORM);
  2516.   sprint(self, "Cujo is here.\n");
  2517.  
  2518.   dogbot.nextthink = time + nextthinktime;
  2519.   dogbot.think = dogbot.th_stand;
  2520.  
  2521.   // remove powerup status from player
  2522.   self.Cujo_avail = 0;
  2523.  
  2524.   // used to determine if Cujo's think is a jumping frame
  2525. };
  2526.  
  2527. /*╔═══════════════════════════════════════════════════════════════════════╗
  2528.   ║                                                                       ║
  2529.   ║ Cujo_Deactivate                                                       ║
  2530.   ║                                                                       ║
  2531.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2532.  
  2533. void () CUJO_Deactivate =
  2534. {
  2535.   spawn_tfog (self.Cujo.origin);
  2536.  
  2537.   if (self.Cujo_view)
  2538.     CUJO_SetPlayerView ();
  2539.  
  2540.   // prevents fire from continuing on the next cujo spawned if he
  2541.   // was burning but not dead when he was deactivated
  2542.   blaze_extinguishentity (self.Cujo);
  2543.  
  2544.   self.Cujo.nextthink = time + nextthinktime;
  2545.   self.Cujo.think = SUB_Remove;
  2546.   self.Cujo_flag = FALSE;
  2547.  
  2548.   sprint (self, "Cujo went back to his doghouse.\n");
  2549. };
  2550.  
  2551. /*╔═══════════════════════════════════════════════════════════════════════╗
  2552.   ║                                                                       ║
  2553.   ║ Cujo_Toggle                                                           ║
  2554.   ║                                                                       ║
  2555.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2556.  
  2557. // ver 1.2 -  Changed spawning rule for deathmatch
  2558. // ver 1.2 -  uhhh... fixed spawning rules for deathmatch :)
  2559.  
  2560. void () CUJO_Toggle =
  2561. {
  2562.   if ((self.Cujo_flag) && (self.Cujo.deadflag == DEAD_DYING)) return;
  2563.  
  2564.   if (deathmatch)
  2565.   {
  2566.     if (self.Cujo_flag)
  2567.     {
  2568.       if ((self.deadflag == DEAD_DYING) || (self.deadflag == DEAD_DEAD))
  2569.       {
  2570.         CUJO_Deactivate ();
  2571.       }
  2572.       else
  2573.         sprint (self, "Cujo can't be sent away in deathmatch.\n");
  2574.     }
  2575.     else if (self.Cujo_avail)
  2576.     {
  2577.       CUJO_Activate ();
  2578.     }
  2579.     else
  2580.       sprint (self, "Cujo is not available.\n");
  2581.   }
  2582.   else
  2583.   {
  2584.     if (!(self.Cujo_flag))
  2585.       CUJO_Activate();
  2586.     else
  2587.       CUJO_Deactivate();
  2588.   }
  2589. };
  2590.  
  2591. /*╔═══════════════════════════════════════════════════════════════════════╗
  2592.   ║                                                                       ║
  2593.   ║ Cujo_AttackToggle                                                     ║
  2594.   ║                                                                       ║
  2595.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2596.  
  2597. void () CUJO_AttackToggle =
  2598. {
  2599.   if (!(self.Cujo_flag))
  2600.   {
  2601.     CUJO_PrintCujoStatus ();
  2602.  
  2603.     return;
  2604.   }
  2605.  
  2606.   if (self.Cujo.Cujo_attack == TRUE)
  2607.   {
  2608.     self.Cujo.Cujo_attack = FALSE;
  2609.     sprint (self, "Cujo will not attack.\n");
  2610.  
  2611.     // reset Cujo's enemy so he doesn't whine even when the enemy goes
  2612.     // out of view.
  2613.  
  2614.     self.Cujo.enemy = world;
  2615.     self.Cujo.oldenemy = world;
  2616.     self.Cujo.goalentity = self;
  2617.     self.Cujo.movetarget = self;
  2618.  
  2619.     self.Cujo.think = self.Cujo.th_stand;
  2620.     self.Cujo.nextthink = time + nextthinktime;
  2621.   }
  2622.   else
  2623.   {
  2624.     self.Cujo.Cujo_attack = TRUE;
  2625.     sprint (self, "Cujo wants blood!\n");
  2626.   }
  2627.  
  2628.   return;
  2629. };
  2630.  
  2631. /*╔═══════════════════════════════════════════════════════════════════════╗
  2632.   ║                                                                       ║
  2633.   ║ Cujo_Attack                                                           ║
  2634.   ║                                                                       ║
  2635.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2636. /*
  2637. void () CUJO_Attack =
  2638. {
  2639.   local entity oldself;
  2640.   local vector org;
  2641.  
  2642.   if (!(self.Cujo_flag))
  2643.   {
  2644.     CUJO_PrintCujoStatus ();
  2645.  
  2646.     return;
  2647.   }
  2648.  
  2649.   oldself = self;
  2650.   self = self.Cujo;
  2651.  
  2652.   if (self.owner.Cujo_auto && self.enemy != world && self.enemy.health > 1
  2653.   && !(self.enemy.items & IT_INVISIBILITY) && visible(self.enemy))
  2654.   {
  2655.   }
  2656.   else
  2657.   {
  2658.   }
  2659.  
  2660.   self = oldself;
  2661.  
  2662.   return;
  2663. };
  2664. */
  2665. /*╔═══════════════════════════════════════════════════════════════════════╗
  2666.   ║                                                                       ║
  2667.   ║ Cujo_TeleportHome                                                     ║
  2668.   ║                                                                       ║
  2669.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2670.  
  2671. void () CUJO_TeleportHome =
  2672. {
  2673.   local vector org;
  2674.  
  2675.   if (!(self.Cujo_flag))
  2676.   {
  2677.     CUJO_PrintCujoStatus ();
  2678.  
  2679.     return;
  2680.   }
  2681.   if (self.Cujo_flag)
  2682.     if (self.Cujo.deadflag == DEAD_DYING) return;
  2683.  
  2684.   spawn_tfog (self.Cujo.origin);
  2685.   self.Cujo.ideal_yaw = self.angles * '0 1 0';
  2686.   self.Cujo.angles = '0 0 0';
  2687.   self.Cujo.pausetime = time + 2;
  2688.   self.Cujo.nextthink = time + nextthinktime;
  2689.   self.Cujo.think = self.Cujo.th_stand;
  2690.  
  2691.   org = self.origin + '0 0 0';
  2692.   setorigin(self.Cujo, org);
  2693.  
  2694.   spawn_tfog (self.Cujo.origin);
  2695.   self.Cujo.nextthink = time + nextthinktime;
  2696.   self.Cujo.think = self.Cujo.th_stand;
  2697.  
  2698.   return;
  2699. };
  2700.  
  2701. /*╔═══════════════════════════════════════════════════════════════════════╗
  2702.   ║                                                                       ║
  2703.   ║ Cujo_LightToggle                                                      ║
  2704.   ║                                                                       ║
  2705.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2706.  
  2707.  
  2708. // This will always be called from the players code, so self is always
  2709. // expected to be the player
  2710.  
  2711. void () CUJO_LightToggle =
  2712. {
  2713.   local   float   effect;
  2714.   local   float   bitmask;
  2715.  
  2716.   if (self.Cujo_flag)
  2717.   {
  2718.     effect = EF_DIMLIGHT;
  2719.  
  2720.     bitmask = (effect) & self.Cujo.effects;
  2721.  
  2722.     if (bitmask == 0)
  2723.     {
  2724.       self.Cujo.effects = self.Cujo.effects | effect;
  2725.     }
  2726.     else
  2727.     {
  2728.       bitmask = !(bitmask);
  2729.       self.Cujo.effects = (self.Cujo.effects) & bitmask;
  2730.     }
  2731.   }
  2732.   else
  2733.     CUJO_PrintCujoStatus ();
  2734.  
  2735.   return;
  2736. };
  2737.  
  2738. /*╔═══════════════════════════════════════════════════════════════════════╗
  2739.   ║                                                                       ║
  2740.   ║ Cujo_SetDogView                                                       ║
  2741.   ║                                                                       ║
  2742.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2743.  
  2744. // This will always be called from the players code, so self is always
  2745. // expected to be the player
  2746.  
  2747. void () CUJO_SetDogView =
  2748. {
  2749.   if (self.Cujo_flag)
  2750.   {
  2751.     // Set view point
  2752.     msg_entity = self;
  2753.     WriteByte (MSG_ONE, 5);                 // SVC, set the viewport
  2754.     WriteEntity (MSG_ONE, self.Cujo);
  2755.  
  2756.     // set the view angles to Cujo's view angles
  2757. //    WriteByte (MSG_ONE, 10);               // SVC, set view angles
  2758. //    WriteAngle(MSG_ONE, self.Cujo.angles_x);
  2759. //    WriteAngle(MSG_ONE, self.Cujo.angles_y);
  2760. //    WriteAngle(MSG_ONE, self.Cujo.angles_z);
  2761.  
  2762.     self.weaponmodel = "";
  2763.     self.weaponframe = 0;
  2764.  
  2765.     self.Cujo_view = TRUE;
  2766.  
  2767.     old_player_angles = self.angles;
  2768.  
  2769.     // turn to Cujo's angles...  I guess
  2770.     self.angles = self.Cujo.angles;
  2771.     self.fixangle = TRUE;        // turn this way immediately
  2772.  
  2773.   }
  2774.   else
  2775.     CUJO_PrintCujoStatus ();
  2776.  
  2777.   return;
  2778.  
  2779. };
  2780.  
  2781. /*╔═══════════════════════════════════════════════════════════════════════╗
  2782.   ║                                                                       ║
  2783.   ║ Cujo_SetPlayerView                                                    ║
  2784.   ║                                                                       ║
  2785.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2786.  
  2787.  
  2788. // This will always be called from the players code, so self is always
  2789. // expected to be the player
  2790.  
  2791. void () CUJO_SetPlayerView =
  2792. {
  2793.   if (self.Cujo_flag)
  2794.   {
  2795.     // Set view point
  2796.     msg_entity = self;
  2797.     WriteByte (MSG_ONE, 5);                 // service, set the viewport
  2798.     WriteEntity (MSG_ONE, self);
  2799.  
  2800. //    WriteByte (MSG_ONE, 10);               // SVC, set view angles
  2801. //    WriteAngle(MSG_ONE, self.angles_x);    // tilt
  2802. //    WriteAngle(MSG_ONE, self.angles_y);    // yaw
  2803. //    WriteAngle(MSG_ONE, self.angles_z);    // flip
  2804.  
  2805.     // reset the players weapon model
  2806.     self.Cujo_view = FALSE;
  2807.     W_SetCurrentAmmo ();
  2808.  
  2809.     self.angles = old_player_angles;
  2810.     self.fixangle = TRUE;        // turn this way immediately
  2811.   }
  2812.   else
  2813.     CUJO_PrintCujoStatus ();
  2814.  
  2815.   return;
  2816. };
  2817.  
  2818. /*╔═══════════════════════════════════════════════════════════════════════╗
  2819.   ║                                                                       ║
  2820.   ║ Cujo_Stay                                                             ║
  2821.   ║                                                                       ║
  2822.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2823.  
  2824. // always called from player code.  Toggles self.Cujo.Cujo_stay which
  2825. // is used in follow routines to determine if Cujo should stay or follow
  2826.  
  2827. void () CUJO_Stay =
  2828. {
  2829.   if (self.Cujo_flag)
  2830.   {
  2831.     // toggle Cujo's stay flag
  2832.     if (self.Cujo.Cujo_stay == TRUE)
  2833.     {
  2834.       self.Cujo.Cujo_stay = FALSE;
  2835.       sprint (self, "Cujo is following.\n");
  2836.     }
  2837.     else
  2838.     {
  2839.       self.Cujo.Cujo_stay = TRUE;
  2840.       sprint (self, "Cujo is staying.\n");
  2841.     }
  2842.  
  2843.   }
  2844.   else
  2845.     CUJO_PrintCujoStatus ();
  2846.  
  2847.   return;
  2848. };
  2849.  
  2850. /*╔═══════════════════════════════════════════════════════════════════════╗
  2851.   ║                                                                       ║
  2852.   ║ Cujo_GiveStatus                                                       ║
  2853.   ║                                                                       ║
  2854.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2855.  
  2856. // called from player code, ie. self = player
  2857.  
  2858. void () CUJO_GiveStatus =
  2859. {
  2860.   local   string  str_temp;
  2861.   local   float   ftemp;
  2862.  
  2863.   if (self.Cujo_flag)
  2864.   {
  2865.     if (visible (self.Cujo))
  2866.     {
  2867.       sprint (self, "Cujo's health is ");
  2868.  
  2869.       ftemp = self.Cujo.health / 2;
  2870.  
  2871.       str_temp = ftos (ftemp);
  2872.       sprint (self, str_temp);
  2873.       sprint (self, "%.\n");
  2874.  
  2875.       sprint (self, "His armor is ");
  2876.  
  2877.       ftemp = self.Cujo.armorvalue / 2;
  2878.  
  2879.       str_temp = ftos (ftemp);
  2880.       sprint (self, str_temp);
  2881.       sprint (self, "%.\n");
  2882.     }
  2883.     else
  2884.     {
  2885.       sprint (self, "You must be able to see Cujo to determine his status.\n");
  2886.     }
  2887.   }
  2888.   else
  2889.     CUJO_PrintCujoStatus ();
  2890.  
  2891.   return;
  2892. };
  2893.  
  2894. /*
  2895. ======================
  2896. CUJO_ShowSkin
  2897. ======================
  2898. */
  2899.  
  2900. void () CUJO_ShowSkin =
  2901. {
  2902.   sprint (self, "Skin #");
  2903.   temp_text = ftos (self.Cujo.skin);
  2904.   sprint (self, temp_text);
  2905.   sprint (self, " selected.\n");
  2906. };
  2907.  
  2908. /*
  2909. ======================
  2910. CUJO_SkinUp
  2911. ======================
  2912. */
  2913. void () CUJO_SkinUp =
  2914. {
  2915.   self.Cujo.skin = self.Cujo.skin + 1;
  2916.   if (self.Cujo.skin > 15)
  2917.     self.Cujo.skin = 0;
  2918.  
  2919.   CUJO_ShowSkin ();
  2920. };
  2921.  
  2922. /*
  2923. ======================
  2924. CUJO_SkinDown
  2925. ======================
  2926. */
  2927. void () CUJO_SkinDown =
  2928. {
  2929.   self.Cujo.skin = self.Cujo.skin - 1;
  2930.   if (self.Cujo.skin < 0)
  2931.     self.Cujo.skin = 15;
  2932.  
  2933.   CUJO_ShowSkin ();
  2934. };
  2935.  
  2936. /*╔═══════════════════════════════════════════════════════════════════════╗
  2937.   ║                                                                       ║
  2938.   ║ Cujo_KillAllMonsters                                                  ║
  2939.   ║                                                                       ║
  2940.   ╚═══════════════════════════════════════════════════════════════════════╝*/
  2941.  
  2942. // to make debugging maps easier -- no having to fight stuff
  2943.  
  2944. void () CUJO_KillAllMonsters =
  2945. {
  2946.   local   entity  head;
  2947.  
  2948.   head = nextent (world);
  2949.  
  2950.   while (head)
  2951.   {
  2952.     if ((head.classname == "monster_army")
  2953.     || (head.classname == "monster_demon1")
  2954.     || (head.classname == "monster_dog")
  2955.     || (head.classname == "monster_dragon")
  2956.     || (head.classname == "monster_enforcer")
  2957.     || (head.classname == "monster_fish")
  2958.     || (head.classname == "monster_hell_knight")
  2959.     || (head.classname == "monster_knight")
  2960.     || (head.classname == "monster_ogre")
  2961.     || (head.classname == "monster_oldone")
  2962.     || (head.classname == "monster_shalrath")
  2963.     || (head.classname == "monster_shambler")
  2964.     || (head.classname == "monster_tarbaby")
  2965.     || (head.classname == "monster_vomit")
  2966.     || (head.classname == "monster_wizard"))
  2967.     {
  2968.       if (random () < 0.2)
  2969.         T_Damage (head, self, self, head.health + 100);
  2970.       else
  2971.         T_Damage (head, self, self, head.health + 1);
  2972.       bprint ("kill ");
  2973.     }
  2974.     else if (head.classname == "monster_zombie")
  2975.       T_Damage (head, self, self, head.health + 50);
  2976.  
  2977.     head = nextent (head);
  2978.    }
  2979.    bprint ("Everything is dead.\n");
  2980. };
  2981.  
  2982. void () CUJO_CheckImpulses=
  2983. {
  2984.   local   float   bitmask;
  2985.  
  2986.   if (self.impulse == CUJO_TOGGLE)
  2987.     CUJO_Toggle();
  2988.  
  2989.   if (self.impulse == CUJO_TELEPORT)
  2990.   {
  2991.     CUJO_TeleportHome();
  2992.   }
  2993.  
  2994.   if (self.impulse == CUJO_LIGHT_TOGGLE)
  2995.   {
  2996.     CUJO_LightToggle ();
  2997.   }
  2998.  
  2999.   if (self.impulse == CUJO_ATTACK_TOGGLE)
  3000.   {
  3001.     CUJO_AttackToggle ();
  3002.   }
  3003.  
  3004. // NOT WORKING YET!  View changes, but angles are not updated and mouse
  3005. // control of view remains.
  3006.  
  3007.   if (self.impulse == CUJO_VIEW_TOGGLE)
  3008.   {
  3009.     if (self.Cujo_view == FALSE)
  3010.     {
  3011.       CUJO_SetDogView ();
  3012.     }
  3013.     else
  3014.     {
  3015.       CUJO_SetPlayerView ();
  3016.     }
  3017.   }
  3018.  
  3019.  
  3020.   if (self.impulse == CUJO_STAY_TOGGLE)
  3021.   {
  3022.     CUJO_Stay ();
  3023.   }
  3024.  
  3025.   if (self.impulse == CUJO_GIVE_STATUS)
  3026.   {
  3027.     CUJO_GiveStatus ();
  3028.   }
  3029.  
  3030.   // debugging impulse
  3031.  
  3032.   if (self.impulse == CUJO_KILL_ALL)
  3033.   {
  3034.     CUJO_KillAllMonsters ();
  3035.   }
  3036.  
  3037. //  if ((self.Cujo_flag) & (self.impulse == MULTISKIN_UP))
  3038. //    CUJO_SkinUp ();
  3039. //  else if ((self.Cujo_flag) & (self.impulse == MULTISKIN_DOWN))
  3040. //    CUJO_SkinDown ();
  3041.  
  3042.  
  3043.   return;
  3044. };
  3045.